From bda49826cfd5a3c8b592e0987bb7a9a5eea9c158 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Apr 2026 12:26:21 +0200 Subject: [PATCH 01/60] Backends: OpenGL3: build fix for WebGL/ES2/Emscripten. (#9378) Amend 6b05f71 which was incorrectly labelled. --- backends/imgui_impl_opengl3.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index c950c7a0..261ab00e 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -255,7 +255,9 @@ struct ImGui_ImplOpenGL3_Data bool UseBufferSubData; bool UseTexParameterToSetSampler; GLuint NextSampler; // Used if !HasBindSampler && UseTexParameterToSetSampler. - GLuint TexSamplers[2]; // Used if IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER && HasBindSimpler (0=linear, 1=nearest). +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + GLuint TexSamplers[2]; // Used if HasBindSimpler. (0=linear, 1=nearest) +#endif ImVector TempBuffer; @@ -404,8 +406,13 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid // Draw callbacks static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[0]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } } static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[1]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } } +#else +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } +#endif // OpenGL3 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. @@ -944,7 +951,9 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() { ImGui_ImplOpenGL3_InitLoader(); ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER if (bd->TexSamplers[0]) { glDeleteSamplers(2, &bd->TexSamplers[0]); bd->TexSamplers[0] = bd->TexSamplers[1] = 0; } +#endif if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; } if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; } if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; } From db23a78c60a5da81127aad87cd0fd3d954c7197c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Apr 2026 13:52:36 +0200 Subject: [PATCH 02/60] Internals: store ImGuiItemStatusFlags_EditedInternal bypassing ImGuiItemFlags_NoMarkEdited. Convenient if the same signal is not passed via e.g. return value. (#8665, #9299, #8065, #3946, #6284, #9117) --- imgui.cpp | 7 ++++--- imgui_internal.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1635e18a..1829d4fc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4753,8 +4753,12 @@ void ImGui::MarkItemEdited(ImGuiID id) // This marking is to be able to provide info for IsItemDeactivatedAfterEdit(). // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data. ImGuiContext& g = *GImGui; + + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_EditedInternal; if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited) return; + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; + if (g.ActiveId == id || g.ActiveId == 0) { // FIXME: Can't we fully rely on LastItemData yet? @@ -4768,9 +4772,6 @@ void ImGui::MarkItemEdited(ImGuiID id) // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714) // FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction. IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive)); - - //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) diff --git a/imgui_internal.h b/imgui_internal.h index 70589997..e1b27f2e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1005,6 +1005,7 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid. ImGuiItemStatusFlags_HasShortcut = 1 << 10, // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd(). //ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb. + ImGuiItemStatusFlags_EditedInternal = 1 << 11, // Similar to ImGuiItemStatusFlags_Edited but bypassing ImGuiItemFlags_NoMarkEdited. // Additional status + semantic for ImGuiTestEngine #ifdef IMGUI_ENABLE_TEST_ENGINE From 10c378cdfcbda8f38f732a68b9f1c274f8eaf7af Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Apr 2026 15:07:53 +0200 Subject: [PATCH 03/60] InputInt, InputFloat, InputScalar: reinstated and fixed ImGuiInputTextFlags_EnterReturnsTrue. (#8665, #9299, #8065, #3946, #6284, #9117) --- docs/CHANGELOG.txt | 8 ++++++++ imgui.h | 2 +- imgui_widgets.cpp | 11 +++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c1d9f2b7..09d33ae9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -89,6 +89,14 @@ Other Changes: - Detect and report error when calling End() instead of EndPopup() on a popup. (#9351) - Child windows with only ImGuiChildFlags_AutoResizeY flag keep using the proportional default ItemWidth. (#9355) +- InputInt, InputFloat, InputScalar: reinstated ImGuiInputTextFlags_EnterReturnsTrue + support which was removed in 1.91.4. (#8665, #9299, #8065, #3946, #6284, #9117) + - Fixed the fact that it didn't return true when validating same value. + - Fixed losing value when tabbing out or losing focus. + - Made it that pressing +/- step buttons also return true, which is in line + with 1.91.4 behavior. + - In a majority of cases you should use IsItemDeactivatedAfterEdit() instead, + but it still has a few edge cases flaws (to be addressed soon). - Multi-Select: - Fixed an issue using Multi-Select within a Table causing column width measurement to be invalid when trailing column contents is not submitted in the last row. (#9341, #8250) diff --git a/imgui.h b/imgui.h index f867f34b..41a93dea 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.8 WIP" -#define IMGUI_VERSION_NUM 19274 +#define IMGUI_VERSION_NUM 19275 #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_widgets.cpp b/imgui_widgets.cpp index 9f3f871f..59d0ceb8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3795,7 +3795,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()! + //IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()! if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; @@ -3832,7 +3832,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data } // Apply - bool value_changed = ret ? DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL) : false; + bool input_edited = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_EditedInternal) != 0; // We would be using 'ret' if ImGuiInputTextFlags_EnterReturnsTrue was not involved. + bool value_changed = input_edited ? DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL) : false; // Step buttons if (has_step_buttons) @@ -3846,13 +3847,13 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data if (ButtonEx("-", ImVec2(button_size, button_size))) { DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); - value_changed = true; + value_changed = ret = true; } SameLine(0, style.ItemInnerSpacing.x); if (ButtonEx("+", ImVec2(button_size, button_size))) { DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step); - value_changed = true; + value_changed = ret = true; } PopItemFlag(); if (flags & ImGuiInputTextFlags_ReadOnly) @@ -3874,6 +3875,8 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data if (value_changed) MarkItemEdited(g.LastItemData.ID); + if (flags & ImGuiInputTextFlags_EnterReturnsTrue) + return ret; return value_changed; } From e3033c397503f85bdc6eb82c75c0a381cebaac96 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 24 Apr 2026 17:38:51 +0200 Subject: [PATCH 04/60] Examples: moving example_win32_directx11 on top of the .sln makes it the default selected project. --- examples/imgui_examples.sln | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index 96087aea..3deffd15 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -3,12 +3,12 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32616.157 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx11", "example_win32_directx11\example_win32_directx11.vcxproj", "{9F316E83-5AE5-4939-A723-305A94F48005}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx9", "example_win32_directx9\example_win32_directx9.vcxproj", "{4165A294-21F2-44CA-9B38-E3F935ABADF5}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx10", "example_win32_directx10\example_win32_directx10.vcxproj", "{345A953E-A004-4648-B442-DC5F9F11068C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx11", "example_win32_directx11\example_win32_directx11.vcxproj", "{9F316E83-5AE5-4939-A723-305A94F48005}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_directx12", "example_win32_directx12\example_win32_directx12.vcxproj", "{B4CF9797-519D-4AFE-A8F4-5141A6B521D3}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_glfw_opengl2", "example_glfw_opengl2\example_glfw_opengl2.vcxproj", "{9CDA7840-B7A5-496D-A527-E95571496D18}" From ab36fbaf48393399ea689b9ed28e989e265e0b66 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 27 Apr 2026 18:23:56 +0200 Subject: [PATCH 05/60] Drag and Drop, Style: added ImGuiStyleVar_DragDropTargetRounding. (#9056) + readded rounding arg to RenderDragDropTargetRectEx(). --- docs/CHANGELOG.txt | 1 + imgui.cpp | 11 ++++++----- imgui.h | 3 ++- imgui_internal.h | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 09d33ae9..d7bffe78 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -129,6 +129,7 @@ Other Changes: - Style: - Fixed vertical scrollbar top coordinates when using thick borders on windows with no title bar and no menu bar. (#9366) + - Added ImGuiStyleVar_DragDropTargetRounding. (#9056) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Assert when using MergeMode with an explicit size over a target font with an implicit diff --git a/imgui.cpp b/imgui.cpp index 1829d4fc..d3156385 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3659,6 +3659,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)}, // ImGuiStyleVar_TreeLinesSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)}, // ImGuiStyleVar_TreeLinesRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DragDropTargetRounding)}, // ImGuiStyleVar_DragDropTargetRounding { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorSize)}, // ImGuiStyleVar_SeparatorSize @@ -15118,7 +15119,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop { ImRect bb = g.DragDropTargetRect; bb.Expand(-3.5f); - RenderDragDropTargetRectEx(GetForegroundDrawList(), bb); + RenderDragDropTargetRectEx(GetForegroundDrawList(), bb, g.Style.DragDropTargetRounding); } else if (draw_target_rect) { @@ -15149,16 +15150,16 @@ void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb) bool push_clip_rect = !window->ClipRect.Contains(bb_display); if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); - RenderDragDropTargetRectEx(window->DrawList, bb_display); + RenderDragDropTargetRectEx(window->DrawList, bb_display, g.Style.DragDropTargetRounding); if (push_clip_rect) window->DrawList->PopClipRect(); } -void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb) +void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, float rounding) { ImGuiContext& g = *GImGui; - draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), g.Style.DragDropTargetRounding, 0); - draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), g.Style.DragDropTargetRounding, 0, g.Style.DragDropTargetBorderSize); + draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), rounding, 0); + draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), rounding, 0, g.Style.DragDropTargetBorderSize); } const ImGuiPayload* ImGui::GetDragDropPayload() diff --git a/imgui.h b/imgui.h index 41a93dea..9699ebf2 100644 --- a/imgui.h +++ b/imgui.h @@ -1852,6 +1852,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2 TableAngledHeadersTextAlign ImGuiStyleVar_TreeLinesSize, // float TreeLinesSize ImGuiStyleVar_TreeLinesRounding, // float TreeLinesRounding + ImGuiStyleVar_DragDropTargetRounding, // float DragDropTargetRounding ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_SeparatorSize, // float SeparatorSize @@ -2328,7 +2329,7 @@ struct ImGuiStyle ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes. float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line. - float DragDropTargetRounding; // Radius of the drag and drop target frame. + float DragDropTargetRounding; // Radius of the drag and drop target frame. When <0.0f: use FrameRounding. float DragDropTargetBorderSize; // Thickness of the drag and drop target border. float DragDropTargetPadding; // Size to expand the drag and drop target from actual target item size. float ColorMarkerSize; // Size of R/G/B/A color markers for ColorEdit4() and for Drags/Sliders when using ImGuiSliderFlags_ColorMarkers. diff --git a/imgui_internal.h b/imgui_internal.h index e1b27f2e..b3792abb 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3494,7 +3494,7 @@ namespace ImGui IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); IMGUI_API void RenderDragDropTargetRectForItem(const ImRect& bb); - IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb); + IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, float rounding); // Typing-Select API // (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature) From d1a8995634fa4413b03c56a4772334ea27a967e1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 28 Apr 2026 14:58:29 +0200 Subject: [PATCH 06/60] MultiSelect: Box-Select + Tables: fixed when using SpanAllColumns paths. (#9383, #7994) Amend ac88294 + d7b40ab --- imgui_widgets.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 59d0ceb8..ecad830a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8367,11 +8367,20 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) if (ImGuiTable* table = g.CurrentTable) if (table->CurrentColumn != -1) { - // FIXME: We cannot use current ClipRect as it includes HostClipRect. - // A more generic version would be nice, but window->WorkRect.Min/Max exclude CellPadding. (#7994) + // FIXME: We cannot solely use current ClipRect as it includes HostClipRect. + // However we account for ClipRect being larger than current column (e.g. when using SpanAllColumns) + // A more generic version would be nice, but window->WorkRect.Min/Max exclude CellPadding. (#7994, #9383) ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; item_rect.Min.x = ImMax(item_rect.Min.x, column->MinX); item_rect.Max.x = ImMin(item_rect.Max.x, column->MaxX); + float clip_min_x = (g.LastItemData.ItemFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect.Min.x : window->ClipRect.Min.x; + float clip_max_x = (g.LastItemData.ItemFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect.Max.x : window->ClipRect.Max.x; + if (clip_min_x != clip_max_x) // When zero sized we expect that bounds have been clamped and thus are unreliable + { + item_rect.Min.x = ImMax(item_rect.Min.x, clip_min_x); + item_rect.Max.x = ImMin(item_rect.Max.x, clip_max_x); + } + } const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(item_rect); const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(item_rect); From dee9b15bbec8c8a7120e4dfddf73b35ae60947f1 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 27 Apr 2026 16:06:33 +0900 Subject: [PATCH 07/60] Backends: Metal: add sampler states and DrawCallback_SetSamplerLinear / DrawCallback_SetSamplerNearest callbacks. (#9381, #9371, #9378) --- backends/imgui_impl_metal.h | 2 -- backends/imgui_impl_metal.mm | 33 +++++++++++++++++++++++++++------ docs/CHANGELOG.txt | 4 ++-- imgui.h | 8 ++++---- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/backends/imgui_impl_metal.h b/backends/imgui_impl_metal.h index 879fadcf..8af2c8b6 100644 --- a/backends/imgui_impl_metal.h +++ b/backends/imgui_impl_metal.h @@ -5,8 +5,6 @@ // [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// Missing features or Issues: -// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index db2fcd3c..652668c7 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -5,8 +5,6 @@ // [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// Missing features or Issues: -// [ ] Renderer: Missing support for DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest callbacks. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -18,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-28: Added support for standard draw callbacks (in platform_io): DrawCallback_SetSamplerLinear and DrawCallback_SetSamplerNearest. (#9378, #9381) // 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState (others are not yet supported). (#9378) // 2026-04-14: Metal: use a dedicated bufferCacheLock to avoid crashing when bufferCache is replaced by a new object while being used for @synchronize(). (#9367) // 2026-04-03: Metal: avoid redundant vertex buffer bind in SetupRenderState. (#9343) @@ -79,6 +78,8 @@ @interface MetalContext : NSObject @property (nonatomic, strong) id device; @property (nonatomic, strong) id depthStencilState; +@property (nonatomic, strong) id samplerStateLinear; +@property (nonatomic, strong) id samplerStateNearest; @property (nonatomic, strong) FramebufferDescriptor* framebufferDescriptor; // framebuffer descriptor for current frame; transient @property (nonatomic, strong) NSMutableDictionary* renderPipelineStateCache; // pipeline cache; keyed on framebuffer descriptors @property (nonatomic, strong) NSMutableArray* bufferCache; @@ -91,6 +92,7 @@ struct ImGui_ImplMetal_Data { MetalContext* SharedMetalContext; + id RenderCommandEncoder; ImGui_ImplMetal_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -185,12 +187,15 @@ static void ImGui_ImplMetal_SetupRenderState(ImDrawData* draw_data, idSharedMetalContext.samplerStateLinear atIndex:0]; [commandEncoder setVertexBuffer:vertexBuffer.buffer offset:vertexBufferOffset atIndex:0]; } // Draw callbacks -static void ImGui_ImplMetal_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplMetal_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplMetal_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); [bd->RenderCommandEncoder setFragmentSamplerState:bd->SharedMetalContext.samplerStateLinear atIndex:0]; } +static void ImGui_ImplMetal_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); [bd->RenderCommandEncoder setFragmentSamplerState:bd->SharedMetalContext.samplerStateNearest atIndex:0]; } // Metal Render function. void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id commandBuffer, id commandEncoder) @@ -228,6 +233,7 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id MetalBuffer* vertexBuffer = [ctx dequeueReusableBufferOfLength:vertexBufferLength device:commandBuffer.device]; MetalBuffer* indexBuffer = [ctx dequeueReusableBufferOfLength:indexBufferLength device:commandBuffer.device]; + bd->RenderCommandEncoder = commandEncoder; ImGui_ImplMetal_SetupRenderState(draw_data, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, 0); // Will project scissor/clipping rectangles into framebuffer space @@ -306,6 +312,7 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id [sharedMetalContext.bufferCache addObject:indexBuffer]; } }]; + bd->RenderCommandEncoder = nil; } static void ImGui_ImplMetal_DestroyTexture(ImTextureData* tex) @@ -382,7 +389,17 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id device) depthStencilDescriptor.depthWriteEnabled = NO; depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; bd->SharedMetalContext.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; + MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init]; + samplerDescriptor.minFilter = MTLSamplerMinMagFilterLinear; + samplerDescriptor.magFilter = MTLSamplerMinMagFilterLinear; + samplerDescriptor.mipFilter = MTLSamplerMipFilterLinear; + bd->SharedMetalContext.samplerStateLinear = [device newSamplerStateWithDescriptor:samplerDescriptor]; + samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest; + samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest; + samplerDescriptor.mipFilter = MTLSamplerMipFilterNearest; + bd->SharedMetalContext.samplerStateNearest = [device newSamplerStateWithDescriptor:samplerDescriptor]; #ifdef IMGUI_IMPL_METAL_CPP + [samplerDescriptor release]; [depthStencilDescriptor release]; #endif @@ -399,6 +416,8 @@ void ImGui_ImplMetal_DestroyDeviceObjects() ImGui_ImplMetal_DestroyTexture(tex); [bd->SharedMetalContext.renderPipelineStateCache removeAllObjects]; + bd->SharedMetalContext.samplerStateLinear = nil; + bd->SharedMetalContext.samplerStateNearest = nil; } bool ImGui_ImplMetal_Init(id device) @@ -415,6 +434,8 @@ bool ImGui_ImplMetal_Init(id device) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.DrawCallback_ResetRenderState = ImGui_ImplMetal_DrawCallback_ResetRenderState; + platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplMetal_DrawCallback_SetSamplerLinear; + platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplMetal_DrawCallback_SetSamplerNearest; bd->SharedMetalContext = [[MetalContext alloc] init]; bd->SharedMetalContext.device = device; @@ -599,9 +620,9 @@ void ImGui_ImplMetal_Shutdown() "}\n" "\n" "fragment half4 fragment_main(VertexOut in [[stage_in]],\n" - " texture2d texture [[texture(0)]]) {\n" - " constexpr sampler linearSampler(coord::normalized, min_filter::linear, mag_filter::linear, mip_filter::linear);\n" - " half4 texColor = texture.sample(linearSampler, in.texCoords);\n" + " texture2d texture [[texture(0)]],\n" + " sampler textureSampler [[sampler(0)]]) {\n" + " half4 texColor = texture.sample(textureSampler, in.texCoords);\n" " return half4(in.color) * texColor;\n" "}\n"; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d7bffe78..5b12ba40 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -149,7 +149,7 @@ Other Changes: - DX10: Reset SetSamplerLinear SetSamplerNearest - DX11: Reset SetSamplerLinear SetSamplerNearest - DX12: Reset SetSamplerLinear SetSamplerNearest - - Metal: Reset *missing* *missing* + - Metal: Reset SetSamplerLinear SetSamplerNearest - OpenGL2: Reset SetSamplerLinear SetSamplerNearest - OpenGL3+: Reset SetSamplerLinear SetSamplerNearest - SDLGPU3: Reset SetSamplerLinear SetSamplerNearest @@ -157,7 +157,7 @@ Other Changes: - SDLRenderer3: Reset SetSamplerLinear SetSamplerNearest - Vulkan: Reset SetSamplerLinear SetSamplerNearest - WebGPU: Reset SetSamplerLinear SetSamplerNearest - (Vulkan backend by @yaz0r, others by @ocornut) + (Vulkan backend by @yaz0r, Metal by @ssh4net, others by @ocornut) - GLFW: added a Win32-specific implementation of `ImGui_ImplGlfw_GetContentScaleXXXX` functions for legacy GLFW 3.2. (#9003) - Metal: avoid redundant vertex buffer bind in `SetupRenderState()`, which leads diff --git a/imgui.h b/imgui.h index 9699ebf2..51be8c55 100644 --- a/imgui.h +++ b/imgui.h @@ -4018,11 +4018,11 @@ struct ImGuiPlatformIO // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure. void* Renderer_RenderState; - // Standard draw callbacks + // Standard draw callbacks provided by renderer backend. ImDrawCallback DrawCallback_ResetRenderState; // Request to reset the graphics/render state. - ImDrawCallback DrawCallback_SetSamplerLinear; // Request to set current texture sampling to Linear - ImDrawCallback DrawCallback_SetSamplerNearest; // Request to set current texture sampling to Nearest/Point - //ImDrawCallback DrawCallback_SetSamplerCustom; // Request to set current texture sampling using Backend Specific data. + ImDrawCallback DrawCallback_SetSamplerLinear; // Request backend to set texture sampling to Linear. + ImDrawCallback DrawCallback_SetSamplerNearest; // Request backend to set texture sampling to Nearest/Point. + //ImDrawCallback DrawCallback_SetSamplerCustom; // Request backend to set texture sampling using Backend Specific data. //------------------------------------------------------------------ // Output From 865a6dfa59052188979797117e0ded2f01f96c42 Mon Sep 17 00:00:00 2001 From: "Alexander \"FireFox\" Ong" Date: Tue, 28 Apr 2026 20:44:20 +1000 Subject: [PATCH 08/60] InputScalar: fixed not parsing user input when the display format is configured not to show the scalar value. (#9385) Useful e.g. for displaying "mixed" inputs, where a single field might represent multiple different values. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5b12ba40..dce8025f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -97,6 +97,8 @@ Other Changes: with 1.91.4 behavior. - In a majority of cases you should use IsItemDeactivatedAfterEdit() instead, but it still has a few edge cases flaws (to be addressed soon). +- InputInt, InputFloat, InputScalar: allow passing a format string that does not display + the scalar value. Parsing input with default format for the type. (#9385) [@FireFox2000000] - Multi-Select: - Fixed an issue using Multi-Select within a Table causing column width measurement to be invalid when trailing column contents is not submitted in the last row. (#9341, #8250) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ecad830a..d05857d3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2375,12 +2375,17 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void // Sanitize format // - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf - // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %. char format_sanitized[32]; if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) + { format = type_info->ScanFmt; + } else + { format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_COUNTOF(format_sanitized)); + if (format[0] == '\0') + format = type_info->ScanFmt; // Format doesn't want us to show the number currently, but we still need to parse the resulting input + } // Small types need a 32-bit buffer to receive the result from scanf() int v32 = 0; From c0b693b1d494cd4746f1b18221c58d76a76c46a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 28 Apr 2026 15:56:50 +0200 Subject: [PATCH 09/60] MultiSelect: Box-Select + Tables: fixed when using SpanAllColumns paths. (#9383, #7994) Amend d1a8995 which didn't fix the thing it claimed to fix, as my naive last minute refactor broke it. --- imgui_widgets.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d05857d3..5f8749ca 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8376,16 +8376,19 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // However we account for ClipRect being larger than current column (e.g. when using SpanAllColumns) // A more generic version would be nice, but window->WorkRect.Min/Max exclude CellPadding. (#7994, #9383) ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; - item_rect.Min.x = ImMax(item_rect.Min.x, column->MinX); - item_rect.Max.x = ImMin(item_rect.Max.x, column->MaxX); float clip_min_x = (g.LastItemData.ItemFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect.Min.x : window->ClipRect.Min.x; float clip_max_x = (g.LastItemData.ItemFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect.Max.x : window->ClipRect.Max.x; if (clip_min_x != clip_max_x) // When zero sized we expect that bounds have been clamped and thus are unreliable { - item_rect.Min.x = ImMax(item_rect.Min.x, clip_min_x); - item_rect.Max.x = ImMin(item_rect.Max.x, clip_max_x); + item_rect.Min.x = ImMax(item_rect.Min.x, ImMin(column->MinX, clip_min_x)); + item_rect.Max.x = ImMin(item_rect.Max.x, ImMax(column->MaxX, clip_max_x)); } - + else + { + item_rect.Min.x = ImMax(item_rect.Min.x, column->MinX); + item_rect.Max.x = ImMin(item_rect.Max.x, column->MaxX); + } + //GetForegroundDrawList()->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255, 0, 255, 255)); } const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(item_rect); const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(item_rect); From 691b89baae95d56df42a59b031c9d19685b7ab2f Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Tue, 14 Apr 2026 12:30:16 +0300 Subject: [PATCH 10/60] ImDrawList: added AddLineH(), AddLineV() helpers. (#9360) This commit is aimed to be a lossless transform. Further layout fixes in subsequent commits. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 22 +++++++++++----------- imgui.h | 2 ++ imgui_demo.cpp | 8 ++++---- imgui_draw.cpp | 20 +++++++++++++++++++- imgui_tables.cpp | 18 +++++++++--------- imgui_widgets.cpp | 14 +++++++------- 7 files changed, 53 insertions(+), 32 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dce8025f..844dc031 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -74,6 +74,7 @@ Other Changes: (#9378, #9371, #3590, #8926, #2973, #7485, #7468, #6969, #5118, #7616, #9173, #8322, #7230, #5999, #6452, #5156, #7342, #7592, #7511) - Made AddCallback() user data default to Null for convenience. + - Added AddLineH(), AddLineV() helpers to draw horizontal and vertical lines. [@memononen] - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. diff --git a/imgui.cpp b/imgui.cpp index d3156385..34b28185 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3927,8 +3927,8 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con text_end_full = FindRenderedTextEnd(text); const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); - //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255)); - //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255)); + //draw_list->AddLineV(pos_max.x, pos_min.y - 4, pos_max.y + 6, IM_COL32(0, 0, 255, 255)); + //draw_list->AddLineV(ellipsis_max_x, pos_min.y - 2, pos_max.y + 3, IM_COL32(0, 255, 0, 255)); // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. if (text_size.x > pos_max.x - pos_min.x) @@ -7155,7 +7155,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) { float y = window->Pos.y + window->TitleBarHeight - 1; - window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize); + window->DrawList->AddLineH(window->Pos.x + border_size * 0.5f, window->Pos.x + window->Size.x - border_size * 0.5f, y, border_col, g.Style.FrameBorderSize); } } @@ -7223,7 +7223,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) - window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + window->DrawList->AddLineH(menu_bar_rect.Min.x + window_border_size * 0.5f, menu_bar_rect.Max.x - window_border_size * 0.5f, menu_bar_rect.Max.y, GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } // Scrollbars @@ -17602,8 +17602,8 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) { ImDrawList* draw_list = GetForegroundDrawList(tab_bar->Window); draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255)); - draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); - draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255)); + draw_list->AddLineV(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y, tab_bar->BarRect.Max.y, IM_COL32(0, 255, 0, 255)); + draw_list->AddLineV(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y, tab_bar->BarRect.Max.y, IM_COL32(0, 255, 0, 255)); } if (open) { @@ -17932,8 +17932,8 @@ void ImGui::DebugDrawCursorPos(ImU32 col) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImVec2 pos = window->DC.CursorPos; - window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f); - window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f); + window->DrawList->AddLineV(pos.x, pos.y - 3.0f, pos.y + 4.0f, col, 1.0f); + window->DrawList->AddLineH(pos.x - 3.0f, pos.x + 4.0f, pos.y, col, 1.0f); } // Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList @@ -17944,9 +17944,9 @@ void ImGui::DebugDrawLineExtents(ImU32 col) float curr_x = window->DC.CursorPos.x; float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y); float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y); - window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f); - window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f); - window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f); + window->DrawList->AddLineH(curr_x - 5.0f, curr_x + 5.0f, line_y1, col, 1.0f); + window->DrawList->AddLineV(curr_x - 0.5f, line_y1, line_y2, col, 1.0f); + window->DrawList->AddLineH(curr_x - 5.0f, curr_x + 5.0f, line_y2, col, 1.0f); } // Draw last item rect in ForegroundDrawList (so it is always visible) diff --git a/imgui.h b/imgui.h index 51be8c55..5a956d44 100644 --- a/imgui.h +++ b/imgui.h @@ -3322,6 +3322,8 @@ struct ImDrawList // In future versions we will use textures to provide cheaper and higher-quality circles. // Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides. IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness = 1.0f); IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index bbdfa1c3..14950d60 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -10138,8 +10138,8 @@ static void ShowExampleAppCustomRendering(bool* p_open) //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th); - draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) + draw_list->AddLineH(x, x + sz, y, col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) + draw_list->AddLineV(x, y, y + sz, col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line // Path @@ -10278,9 +10278,9 @@ static void ShowExampleAppCustomRendering(bool* p_open) { const float GRID_STEP = 64.0f; for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP) - draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40)); + draw_list->AddLineV(canvas_p0.x + x, canvas_p0.y, canvas_p1.y, IM_COL32(200, 200, 200, 40)); for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP) - draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40)); + draw_list->AddLineH(canvas_p0.x, canvas_p1.x, canvas_p0.y + y, IM_COL32(200, 200, 200, 40)); } for (int n = 0; n < points.Size; n += 2) draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index dc814a4a..c7415f96 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1484,6 +1484,24 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th PathStroke(col, 0, thickness); } +void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + PathLineTo(ImVec2(min_x + 0.5f, y + 0.5f)); // Same as AddLine() above. + PathLineTo(ImVec2(max_x + 0.5f, y + 0.5f)); + PathStroke(col, 0, thickness); +} + +void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + PathLineTo(ImVec2(x + 0.5f, min_y + 0.5f)); // Same as AddLine() above. + PathLineTo(ImVec2(x + 0.5f, max_y + 0.5f)); + PathStroke(col, 0, thickness); +} + // p_min = upper-left, p_max = lower-right // Note we don't render 1 pixels sized rectangles properly. void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) @@ -1659,7 +1677,7 @@ void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 co // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); - PathStroke(col, true, thickness); + PathStroke(col, ImDrawFlags_Closed, thickness); } void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index cc910b98..ce225f46 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2074,11 +2074,11 @@ void ImGui::TableEndRow(ImGuiTable* table) // Draw top border if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) - window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size); + window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y1, top_border_col, border_size); // Draw bottom border at the row unfreezing mark (always strong) if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) - window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size); + window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y2, table->BorderColorStrong, border_size); } // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) @@ -2855,7 +2855,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0) draw_y2 = draw_y2_body; if (draw_y2 > draw_y1) - inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size); + inner_drawlist->AddLineV(column->MaxX, draw_y1, draw_y2, TableGetColumnBorderCol(table, order_n, column_n), border_size); } } @@ -2876,13 +2876,13 @@ void ImGui::TableDrawBorders(ImGuiTable* table) } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { - inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size); - inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size); + inner_drawlist->AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size); + inner_drawlist->AddLineV(outer_border.Max.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterH) { - inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size); - inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y, outer_col, border_size); } } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) @@ -2890,7 +2890,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw bottom-most row border between it is above outer border. const float border_y = table->RowPosY2; if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) - inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); + inner_drawlist->AddLineH(table->BorderX1, table->BorderX2, border_y, table->BorderColorLight, border_size); } inner_drawlist->PopClipRect(); @@ -4618,7 +4618,7 @@ void ImGui::EndColumns() // Draw column const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); const float xi = IM_TRUNC(x); - window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col); + window->DrawList->AddLineV(xi, y1 + 1.0f, y2, col); } // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5f8749ca..33b9a4c5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1556,7 +1556,7 @@ bool ImGui::TextLink(const char* label) } float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f); - window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI + window->DrawList->AddLineH(bb.Min.x, bb.Max.x, line_y, GetColorU32(line_colf), 1.0f * (float)(int)g.Style._MainScale); // FIXME-TEXT: Underline mode // FIXME-DPI PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf)); RenderText(bb.Min, label, label_end, false); @@ -1766,9 +1766,9 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float sep1_x2 = label_pos.x - style.ItemSpacing.x; const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x; if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f) - window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness); + window->DrawList->AddLineH(sep1_x1, sep1_x2, seps_y, separator_col, separator_thickness); if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f) - window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); + window->DrawList->AddLineH(sep2_x1, sep2_x2, seps_y, separator_col, separator_thickness); if (g.LogEnabled) LogSetNextTextDecoration("---", NULL); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size); @@ -1778,7 +1778,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end if (g.LogEnabled) LogText("---"); if (separator_thickness > 0.0f) - window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); + window->DrawList->AddLineH(sep1_x1, sep2_x2, seps_y, separator_col, separator_thickness); } } @@ -5638,7 +5638,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031) + draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031) // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) // This is required for some backends (SDL3) to start emitting character/text inputs. @@ -7173,7 +7173,7 @@ void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos) } else { - window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); + window->DrawList->AddLineH(x1, x2, y, GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); } } @@ -7199,7 +7199,7 @@ void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data) float x = ImTrunc(data->DrawLinesX1); if (data->DrawLinesTableColumn != -1) TablePushColumnChannel(data->DrawLinesTableColumn); - window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); + window->DrawList->AddLineV(x, y1, y2, GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); if (data->DrawLinesTableColumn != -1) TablePopColumnChannel(); } From 976c5c0f3a77e20c79d5407efcf94bc9047ee8cf Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 6 May 2026 16:17:17 +0200 Subject: [PATCH 11/60] Textures: extract ImTextureDataQueueUpload() out of ImFontAtlasTextureBlockQueueUpload() (#8465) Not atlas specific, in support of texture system. --- imgui.cpp | 2 +- imgui_draw.cpp | 10 +++++++--- imgui_internal.h | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 34b28185..f4c44939 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9013,7 +9013,7 @@ ImFont* ImGui::GetDefaultFont() return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0]; } -// EXPERIMENTAL: DO NOT USE YET. +// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. void ImGui::RegisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c7415f96..cc497f38 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2973,12 +2973,17 @@ void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, I memcpy(dst_tex->GetPixelsAt(dst_x, dst_y + y), src_tex->GetPixelsAt(src_x, src_y + y), w * dst_tex->BytesPerPixel); } -// Queue texture block update for renderer backend void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h) +{ + ImTextureDataQueueUpload(tex, x, y, w, h); + atlas->TexIsBuilt = false; +} + +// Queue texture block update for renderer backend +void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h) { IM_ASSERT(tex->Status != ImTextureStatus_WantDestroy && tex->Status != ImTextureStatus_Destroyed); IM_ASSERT(x >= 0 && x <= 0xFFFF && y >= 0 && y <= 0xFFFF && w >= 0 && x + w <= 0x10000 && h >= 0 && y + h <= 0x10000); - IM_UNUSED(atlas); ImTextureRect req = { (unsigned short)x, (unsigned short)y, (unsigned short)w, (unsigned short)h }; int new_x1 = ImMax(tex->UpdateRect.w == 0 ? 0 : tex->UpdateRect.x + tex->UpdateRect.w, req.x + req.w); @@ -2991,7 +2996,6 @@ void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, tex->UsedRect.y = ImMin(tex->UsedRect.y, req.y); tex->UsedRect.w = (unsigned short)(ImMax(tex->UsedRect.x + tex->UsedRect.w, req.x + req.w) - tex->UsedRect.x); tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y); - atlas->TexIsBuilt = false; // No need to queue if status is == ImTextureStatus_WantCreate if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates) diff --git a/imgui_internal.h b/imgui_internal.h index b3792abb..918b6277 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3215,7 +3215,7 @@ namespace ImGui IMGUI_API void SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags); // Fonts, drawing - IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET. + IMGUI_API void RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL. IMGUI_API void UnregisterUserTexture(ImTextureData* tex); IMGUI_API void RegisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas); @@ -3977,6 +3977,7 @@ IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h); IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h); +IMGUI_API void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h); IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format); IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status); IMGUI_API const char* ImTextureDataGetFormatName(ImTextureFormat format); From 6df50a0667a35a853da38cf4a4cbcf632325fec9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 27 Apr 2026 22:13:42 +0200 Subject: [PATCH 12/60] (Breaking) DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). thickness<>flags. Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. The aim is to be able to use/add flags to more ImDrawList functions, and making existing functions consistents was deemed very desirable. --- docs/CHANGELOG.txt | 22 ++++++++++++++++- imgui.cpp | 59 +++++++++++++++++++++++++++++++--------------- imgui.h | 19 ++++++++++----- imgui_demo.cpp | 10 ++++---- imgui_draw.cpp | 47 +++++++++++++++++++----------------- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 12 +++++----- 7 files changed, 111 insertions(+), 60 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 844dc031..e0806f14 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,27 @@ Breaking Changes: - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) Redirecting the earlier value into the later one when set, so both old and new code should work. +- DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). + Recap: + - Before: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);` + - After: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0);` + - Before: `void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);` + - After: `void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, float thickness, ImDrawFlags flags = 0);` + - Before: `void ImDrawList::PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f);` + - After: `void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0);` + Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. + Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. + This is not an easy change but it makes ImDrawList function signatures consistent. + As we are aiming to add flags and features to variety of ImDrawList functions, that consistency will become particularly important. + The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. + Effectively the typical call site is changing from: + - Before: `window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size);` + - After: `window->DrawList->AddRect(p_min, p_max, color, rounding, border_size);` + Notes: + - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. + - Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes. + - Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value. + - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. - Backends: - Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914) This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut] @@ -179,7 +200,6 @@ Other Changes: - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. - ----------------------------------------------------------------------- VERSION 1.92.7 (Released 2026-04-02) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index f4c44939..8972baac 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,6 +395,27 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). + Recap: + - Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); + - After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); + - Before: void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); + - After: void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, float thickness, ImDrawFlags flags = 0); + - Before: void ImDrawList::PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f); + - After: void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0); + Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. + Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. + This is not an easy change but it makes ImDrawList function signatures consistent. + As we are aiming to add flags and features to variety of ImDrawList functions, that consistency will become particularly important. + The new order is also more convenient as 'flags' are less frequently used than 'thickness' in real code. + Effectively the typical call site is changing from: + - Before: window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size); + - After: window->DrawList->AddRect(p_min, p_max, color, rounding, border_size); + Notes: + - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. + - Users of C++ and other languages with type-checking should be notified at compile-time of any mistakes. + - Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value. + - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. - 2026/04/23 (1.92.8) - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) - 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. @@ -3973,8 +3994,8 @@ void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders const float border_size = g.Style.FrameBorderSize; if (borders && border_size > 0.0f) { - window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); + window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, border_size); } } @@ -3985,8 +4006,8 @@ void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) const float border_size = g.Style.FrameBorderSize; if (border_size > 0.0f) { - window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); + window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, border_size); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, border_size); } } @@ -4021,7 +4042,7 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl const float thickness = 2.0f; if (flags & ImGuiNavRenderCursorFlags_Compact) { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness); + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness); } else { @@ -4030,7 +4051,7 @@ void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFl bool fully_visible = window->ClipRect.Contains(display_rect); if (!fully_visible) window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness); + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, thickness); if (!fully_visible) window->DrawList->PopClipRect(); } @@ -4064,7 +4085,7 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI); float a_max = a_min + IM_PI * 1.65f; draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max); - draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale); + draw_list->PathStroke(col_fill, 3.0f * scale); } draw_list->PopTexture(); } @@ -4937,7 +4958,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flag { g.HoveredIdPreviousFrameItemCount++; if (g.DebugDrawIdConflictsId == id) - window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); + window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, 2.0f); } #endif @@ -5969,7 +5990,7 @@ static void ImGui::RenderDimmedBackgrounds() if (window->DrawList->CmdBuffer.Size == 0) window->DrawList->AddDrawCmd(); window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 3.0f); // FIXME-DPI window->DrawList->PopClipRect(); } } @@ -7129,7 +7150,7 @@ static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f); window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); - window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size); + window->DrawList->PathStroke(border_col, border_size); } static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) @@ -7138,7 +7159,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) const float border_size = window->WindowBorderSize; const ImU32 border_col = GetColorU32(ImGuiCol_Border); if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize); + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, window->WindowBorderSize); else if (border_size > 0.0f) { if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border. @@ -15159,7 +15180,7 @@ void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, { ImGuiContext& g = *GImGui; draw_list->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTargetBg), rounding, 0); - draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), rounding, 0, g.Style.DragDropTargetBorderSize); + draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), rounding, g.Style.DragDropTargetBorderSize); } const ImGuiPayload* ImGui::GetDragDropPayload() @@ -16261,7 +16282,7 @@ void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding); ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y); ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y); - draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f); + draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, 2.0f); draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding); ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y); draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label); @@ -16684,7 +16705,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name); if (IsItemHovered()) - GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); + GetForegroundDrawList(table->OuterWindow)->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 2.0f); Indent(); char buf[128]; for (int rect_n = 0; rect_n < TRT_Count; rect_n++) @@ -16699,7 +16720,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) - GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); + GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 2.0f); } } else @@ -16708,7 +16729,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImFormatString(buf, IM_COUNTOF(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]); Selectable(buf); if (IsItemHovered()) - GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f); + GetForegroundDrawList(table->OuterWindow)->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 2.0f); } } Unindent(); @@ -17116,7 +17137,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n); ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255); float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f; - draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness); + draw_list->AddRect(r.Min, r.Max, col, 0.0f, thickness); } } else @@ -17293,7 +17314,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con { ImDrawListFlags backup_flags = fg_draw_list->Flags; fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles. - fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); + fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0f, ImDrawFlags_Closed); fg_draw_list->Flags = backup_flags; } } @@ -17321,7 +17342,7 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co for (int n = 0; n < 3; n++, idx_n++) vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos)); if (show_mesh) - out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles + out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), 1.0f, ImDrawFlags_Closed); // In yellow: mesh triangles } // Draw bounding boxes if (show_aabb) diff --git a/imgui.h b/imgui.h index 5a956d44..c91a13b1 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.8 WIP" -#define IMGUI_VERSION_NUM 19275 +#define IMGUI_VERSION_NUM 19276 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -3240,16 +3240,15 @@ struct ImDrawListSplitter }; // Flags for ImDrawList functions -// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused) enum ImDrawFlags_ { ImDrawFlags_None = 0, - ImDrawFlags_Closed = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! + ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, @@ -3257,6 +3256,7 @@ enum ImDrawFlags_ ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, + ImDrawFlags_InvalidMask_ = 0x8000000F, }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. @@ -3324,7 +3324,7 @@ struct ImDrawList IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); IMGUI_API void AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness = 1.0f); IMGUI_API void AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size) + IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); IMGUI_API void AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f); @@ -3345,7 +3345,7 @@ struct ImDrawList // General polygon // - Only simple polygons are supported by filling functions (no self-intersections, no holes). // - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience for the user but not used by the main library. - IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, float thickness, ImDrawFlags flags = 0); IMGUI_API void AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col); IMGUI_API void AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col); @@ -3365,7 +3365,7 @@ struct ImDrawList inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } inline void PathFillConcave(ImU32 col) { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } - inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } + inline void PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0) { AddPolyline(_Path.Data, _Path.Size, col, thickness, flags); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle IMGUI_API void PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse @@ -3413,8 +3413,15 @@ struct ImDrawList // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) { AddRect(p_min, p_max, col, rounding, thickness, flags); } // OBSOLETED in 1.92.8: NEW FUNCTION SIGNATURE HAS 'thickness' AND 'flags' SWAPPED. + inline void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) { AddPolyline(points, num_points, col, thickness, flags); } // OBSOLETED in 1.92.8: NEW FUNCTION SIGNATURE HAS 'thickness' AND 'flags' SWAPPED. + inline void PathStroke(ImU32 col, ImDrawFlags flags, float thickness) { PathStroke(col, thickness, flags); } // OBSOLETED in 1.92.8: NEW FUNCTION SIGNATURE HAS 'thickness' AND 'flags' SWAPPED. inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0 inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0 +#else + IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; + IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete; + inline void PathStroke(ImU32 col, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; #endif //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024) //inline void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 14950d60..4adb48fb 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -10131,12 +10131,12 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners - draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, th); x += sz + spacing; // Square + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th); x += sz + spacing; // Square with all rounded corners + draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, th, corners_tl_br); x += sz + spacing; // Square with two rounded corners draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing; // Triangle //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle - PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th); x += sz + spacing; // Concave Shape + PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, th, ImDrawFlags_Closed); x += sz + spacing; // Concave Shape //draw_list->AddPolyline(concave_shape, IM_COUNTOF(concave_shape), col, ImDrawFlags_Closed, th); draw_list->AddLineH(x, x + sz, y, col, th); x += sz + spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) draw_list->AddLineV(x, y, y + sz, col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) @@ -10144,7 +10144,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Path draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f); - draw_list->PathStroke(col, ImDrawFlags_None, th); + draw_list->PathStroke(col, th); x += sz + spacing; // Quadratic Bezier Curve (3 control points) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index cc497f38..093f98a8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -811,7 +811,7 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. -void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness) +void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, float thickness, ImDrawFlags flags) { if (points_count < 2 || (col & IM_COL32_A_MASK) == 0) return; @@ -820,6 +820,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > _FringeScale); + IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); if (Flags & ImDrawListFlags_AntiAliasedLines) { @@ -1441,14 +1442,6 @@ void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDr { if (rounding >= 0.5f) { - // If this assert triggers on legacy code, please update your code replacing hardcoded values with ImDrawFlags_RoundCorners* values. - // - See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section. - // - Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway. - // - Marked obsolete in 1.82 and completely removed in 1.90: - // - Hard coded support for ~0 == ImDrawFlags_RoundCornersAll. - // - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code. - // - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' - IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); if ((flags & ImDrawFlags_RoundCornersMask_) == 0) flags |= ImDrawFlags_RoundCornersAll; @@ -1481,7 +1474,7 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th return; PathLineTo(p1 + ImVec2(0.5f, 0.5f)); PathLineTo(p2 + ImVec2(0.5f, 0.5f)); - PathStroke(col, 0, thickness); + PathStroke(col, thickness); } void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness) @@ -1490,7 +1483,7 @@ void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float th return; PathLineTo(ImVec2(min_x + 0.5f, y + 0.5f)); // Same as AddLine() above. PathLineTo(ImVec2(max_x + 0.5f, y + 0.5f)); - PathStroke(col, 0, thickness); + PathStroke(col, thickness); } void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness) @@ -1499,20 +1492,30 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th return; PathLineTo(ImVec2(x + 0.5f, min_y + 0.5f)); // Same as AddLine() above. PathLineTo(ImVec2(x + 0.5f, max_y + 0.5f)); - PathStroke(col, 0, thickness); + PathStroke(col, thickness); } // p_min = upper-left, p_max = lower-right // Note we don't render 1 pixels sized rectangles properly. -void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness) +void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, float thickness, ImDrawFlags flags) { + // If this assert triggers on legacy code: + // - 1.92.8 (2025/04): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.82.0 (2021/03): changed ImDrawCornerFlags to ImDrawFlags_RoundCornersXXX values. + // If you used hard-coded 1 to 15 or ~0 in flags to configure corner rounding use the new flags! + // - Hard coded support for ~0 == ImDrawFlags_RoundCornersAll. + // - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code. + // - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'. + // See "API BREAKING CHANGES" section for 1.82 and 1.90. + IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values + if ((col & IM_COL32_A_MASK) == 0) return; if (Flags & ImDrawListFlags_AntiAliasedLines) PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags); else PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes. - PathStroke(col, ImDrawFlags_Closed, thickness); + PathStroke(col, thickness, ImDrawFlags_Closed); } void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags) @@ -1556,7 +1559,7 @@ void ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, c PathLineTo(p2); PathLineTo(p3); PathLineTo(p4); - PathStroke(col, ImDrawFlags_Closed, thickness); + PathStroke(col, thickness, ImDrawFlags_Closed); } void ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col) @@ -1579,7 +1582,7 @@ void ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p PathLineTo(p1); PathLineTo(p2); PathLineTo(p3); - PathStroke(col, ImDrawFlags_Closed, thickness); + PathStroke(col, thickness, ImDrawFlags_Closed); } void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col) @@ -1614,7 +1617,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); } - PathStroke(col, ImDrawFlags_Closed, thickness); + PathStroke(col, thickness, ImDrawFlags_Closed); } void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments) @@ -1650,7 +1653,7 @@ void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_ // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments; PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1); - PathStroke(col, ImDrawFlags_Closed, thickness); + PathStroke(col, thickness, ImDrawFlags_Closed); } // Guaranteed to honor 'num_segments' @@ -1677,7 +1680,7 @@ void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 co // Because we are filling a closed shape we remove 1 from the count of segments/points const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1); - PathStroke(col, ImDrawFlags_Closed, thickness); + PathStroke(col, thickness, ImDrawFlags_Closed); } void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments) @@ -1702,7 +1705,7 @@ void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2 PathLineTo(p1); PathBezierCubicCurveTo(p2, p3, p4, num_segments); - PathStroke(col, 0, thickness); + PathStroke(col, thickness); } // Quadratic Bezier takes 3 controls points @@ -1713,7 +1716,7 @@ void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const Im PathLineTo(p1); PathBezierQuadraticCurveTo(p2, p3, num_segments); - PathStroke(col, 0, thickness); + PathStroke(col, thickness); } void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) @@ -6041,7 +6044,7 @@ void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float draw_list->PathLineTo(ImVec2(bx - third, by - third)); draw_list->PathLineTo(ImVec2(bx, by)); draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f)); - draw_list->PathStroke(col, 0, thickness); + draw_list->PathStroke(col, thickness); } // Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index ce225f46..352ed62e 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2872,7 +2872,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const ImU32 outer_col = table->BorderColorStrong; if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 33b9a4c5..b8fcd749 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1151,7 +1151,7 @@ void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const Im else window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); if (g.Style.ImageBorderSize > 0.0f) - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, ImDrawFlags_None, g.Style.ImageBorderSize); + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, g.Style.ImageBorderSize); } void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1) @@ -6352,7 +6352,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; const int vert_start_idx = draw_list->VtxBuffer.Size; draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); - draw_list->PathStroke(col_white, 0, wheel_thickness); + draw_list->PathStroke(col_white, wheel_thickness); const int vert_end_idx = draw_list->VtxBuffer.Size; // Paint colors over existing vertices @@ -6496,7 +6496,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if (g.Style.FrameBorderSize > 0.0f) RenderFrameBorder(bb.Min, bb.Max, rounding); else - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding, 0, 1.0f * (float)(int)g.Style._MainScale); // Color buttons are often in need of some sort of border // FIXME-DPI + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding, 1.0f * (float)(int)g.Style._MainScale); // Color buttons are often in need of some sort of border // FIXME-DPI } // Drag and Drop Source @@ -7169,7 +7169,7 @@ void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos) window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3); if (x1 < x2) window->DrawList->PathLineTo(ImVec2(x2, y)); - window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize); + window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize); } else { @@ -10756,7 +10756,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, float rounding = style.TabRounding; display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9); display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11); - display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize); + display_draw_list->PathStroke(overline_col, style.TabBarOverlineSize); } else { @@ -10860,7 +10860,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9); draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12); draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2)); - draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize); + draw_list->PathStroke(GetColorU32(ImGuiCol_Border), g.Style.TabBorderSize); } } From a70b97ee48b5a83fc3aa4dec479d64233b715363 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 May 2026 16:50:03 +0200 Subject: [PATCH 13/60] Warning fixes. --- imgui.cpp | 2 +- imgui.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 8972baac..20ccc021 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7224,7 +7224,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar if (bg_col & IM_COL32_A_MASK) { ImRect bg_rect(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size); - ImDrawFlags bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom; + ImDrawFlags bg_rounding_flags = (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersBottom; ImDrawList* bg_draw_list = window->DrawList; bg_draw_list->AddRectFilled(bg_rect.Min, bg_rect.Max, bg_col, window_rounding, bg_rounding_flags); } diff --git a/imgui.h b/imgui.h index c91a13b1..376a008d 100644 --- a/imgui.h +++ b/imgui.h @@ -3256,7 +3256,7 @@ enum ImDrawFlags_ ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, - ImDrawFlags_InvalidMask_ = 0x8000000F, + ImDrawFlags_InvalidMask_ = (ImDrawFlags)0x8000000F, }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. From 163b8670c8619be26b7bacabb82448584701b618 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 May 2026 19:55:12 +0200 Subject: [PATCH 14/60] Demo: added image viewer with magnifier and grid. --- docs/CHANGELOG.txt | 3 + imgui_demo.cpp | 158 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 131 insertions(+), 30 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e0806f14..a27c45b3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -166,6 +166,9 @@ Other Changes: - Misc: - Minor optimization: reduce redudant label scanning in common widgets. - Added missing Test Engine hooks for PlotXXX(), VSliderXXX(), TableHeader(). +- Demo: + - Added simple demo for a scrollable/zoomable image viewer with a grid. + Available in `Examples->Image Viewer` and `Widgets->Images`. - Backends: - Added support for new standardized draw callbacks in most backends: (#9378) - Allegro5: Reset n/a n/a diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 4adb48fb..d43d3495 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -73,6 +73,7 @@ Index of this file: // [SECTION] Demo Window / ShowDemoWindow() // [SECTION] DemoWindowMenuBar() // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) +// [SECTION] Helpers: ExampleImageViewer // [SECTION] DemoWindowWidgetsBasic() // [SECTION] DemoWindowWidgetsBullets() // [SECTION] DemoWindowWidgetsCollapsingHeaders() @@ -108,6 +109,7 @@ Index of this file: // [SECTION] User Guide / ShowUserGuide() // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() // [SECTION] Example App: Debug Console / ShowExampleAppConsole() +// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer() // [SECTION] Example App: Debug Log / ShowExampleAppLog() // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() @@ -238,6 +240,7 @@ static void ShowExampleAppAssetsBrowser(bool* p_open); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); +static void ShowExampleAppImageViewer(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data); @@ -308,6 +311,7 @@ struct ImGuiDemoWindowData bool ShowAppConsole = false; bool ShowAppCustomRendering = false; bool ShowAppDocuments = false; + bool ShowAppImageViewer = false; bool ShowAppLog = false; bool ShowAppLayout = false; bool ShowAppPropertyEditor = false; @@ -353,6 +357,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); } if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); } if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); } + if (demo_data.ShowAppImageViewer) { ShowExampleAppImageViewer(&demo_data.ShowAppImageViewer); } if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); } if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); } if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); } @@ -677,6 +682,7 @@ static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data) ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole); ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering); ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments); + ImGui::MenuItem("Image Viewer", NULL, &demo_data->ShowAppImageViewer); ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog); ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor); ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout); @@ -825,6 +831,87 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() return node_L0; } +//----------------------------------------------------------------------------- +// [SECTION] Helpers: ExampleImageViewer +//----------------------------------------------------------------------------- + +struct ExampleImageViewerData +{ + ImU32 ImageBgColor = IM_COL32(100, 100, 100, 255); + ImU32 GridColor = IM_COL32(255, 255, 255, 100); + bool GridEnabled = true; + bool ViewReset = true; + ImVec2 ViewOffset; // in image space + float Zoom = 10.0f; + float ZoomMin = 1.0f; + float ZoomMax = 10000.0f; +}; + +static void ExampleImageViewer_DrawOptions(ExampleImageViewerData* data) +{ + ImGui::SetNextItemShortcut(ImGuiKey_G, ImGuiInputFlags_Tooltip); // | ImGuiInputFlags_RouteGlobal + ImGui::Checkbox("Grid", &data->GridEnabled); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f); + float zoom_100 = data->Zoom * 100.0f; + if (ImGui::DragFloat("##Zoom", &zoom_100, 5.0f, data->ZoomMin * 100.0f, data->ZoomMax * 100.0f, "%.0f%%", ImGuiSliderFlags_AlwaysClamp)) + data->Zoom = zoom_100 / 100.0f; +} + +static void ExampleImageViewer_DrawCanvas(ExampleImageViewerData* data, ImVec2 canvas_size, ImTextureRef image_tex_ref, int image_w, int image_h) +{ + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + IM_ASSERT(canvas_size.x >= 0.0f && canvas_size.y >= 0.0f); + + // Layout canvas + ImGui::InvisibleButton("##Canvas", canvas_size); + ImVec2 canvas_min = ImGui::GetItemRectMin(); + ImVec2 canvas_max = ImGui::GetItemRectMax(); + + if (data->ViewReset) + data->ViewOffset = ImVec2((canvas_size.x * 0.5f / data->Zoom) - 0.5f, (canvas_size.y * 0.5f / data->Zoom) - 0.5f); // Add half a pixel padding + data->ViewReset = false; + + // Handle inputs + ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY); // FIXME: Not while scrolling? + if (ImGui::IsItemHovered() && io.MouseWheel != 0.0f) + data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax); + float zoom = data->Zoom; // (float)(int)ViewZoom; + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) + { + data->ViewOffset.x -= io.MouseDelta.x / zoom; + data->ViewOffset.y -= io.MouseDelta.y / zoom; + } + + // Display image + ImVec2 image_min, image_max; + image_min.x = (float)(int)((canvas_min.x - (data->ViewOffset.x * zoom)) + (canvas_size.x * 0.5f)); + image_min.y = (float)(int)((canvas_min.y - (data->ViewOffset.y * zoom)) + (canvas_size.y * 0.5f)); + image_max.x = (float)(int)(image_min.x + image_w * zoom); + image_max.y = (float)(int)(image_min.y + image_h * zoom); + draw_list->AddRect(ImVec2(canvas_min.x - 1.0f, canvas_min.y - 1.0f), ImVec2(canvas_max.x + 1.0f, canvas_max.y + 1.0f), IM_COL32(255, 255, 255, 255)); + draw_list->PushClipRect(canvas_min, canvas_max, true); + draw_list->AddRectFilled(image_min, image_max, data->ImageBgColor); + if (platform_io.DrawCallback_SetSamplerNearest != NULL) + draw_list->AddCallback(platform_io.DrawCallback_SetSamplerNearest); + draw_list->AddImage(image_tex_ref, image_min, image_max); + if (platform_io.DrawCallback_SetSamplerLinear != NULL) + draw_list->AddCallback(ImGui::GetPlatformIO().DrawCallback_SetSamplerLinear); + + // Display grid lines for visible pixels + if (data->GridEnabled && zoom > 6.0f) + { + const float step = (float)zoom; + for (int px = (int)((canvas_min.x - image_min.x) / step); px <= (int)((canvas_max.x - image_min.x) / step); px++) + draw_list->AddLineV(image_min.x + px * step, canvas_min.y, canvas_max.y, data->GridColor, 1.0f); + for (int py = (int)((canvas_min.y - image_min.y) / step); py <= (int)((canvas_max.y - image_min.y) / step); py++) + draw_list->AddLineH(canvas_min.x, canvas_max.x, image_min.y + py * step, data->GridColor, 1.0f); + } + draw_list->PopClipRect(); +} + //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsBasic() //----------------------------------------------------------------------------- @@ -1802,40 +1889,29 @@ static void DemoWindowWidgetsImages() // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // Grab the current texture identifier used by the font atlas. - ImTextureRef my_tex_id = io.Fonts->TexRef; + ImFontAtlas* atlas = io.Fonts; + ImTextureRef my_tex_id = atlas->TexRef; + float my_tex_w = (float)atlas->TexData->Width; // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it. + float my_tex_h = (float)atlas->TexData->Height; + ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); - // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it. - float my_tex_w = (float)io.Fonts->TexData->Width; - float my_tex_h = (float)io.Fonts->TexData->Height; + // Basic drawing + ImGui::SeparatorText("Image()/ImageWithBg() function"); + ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left + ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right + ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize)); + ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PopStyleVar(); - { - ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left - ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right - ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize)); - ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - if (ImGui::BeginItemTooltip()) - { - float region_sz = 32.0f; - float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; - float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; - float zoom = 4.0f; - if (region_x < 0.0f) { region_x = 0.0f; } - else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; } - if (region_y < 0.0f) { region_y = 0.0f; } - else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; } - ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); - ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); - ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); - ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); - ImGui::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - ImGui::EndTooltip(); - } - ImGui::PopStyleVar(); - } + // Fancy widget + ImGui::SeparatorText("Interactive Image Viewer"); + static ExampleImageViewerData image_viewer; + ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, my_tex_h * 2.0f); + ExampleImageViewer_DrawOptions(&image_viewer); + ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, my_tex_id, (int)my_tex_w, (int)my_tex_h); IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); + ImGui::SeparatorText("Textured Buttons"); ImGui::TextWrapped("And now some textured buttons.."); static int pressed_count = 0; for (int i = 0; i < 8; i++) @@ -9239,6 +9315,28 @@ static void ShowExampleAppConsole(bool* p_open) console.Draw("Example: Console", p_open); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer() +//----------------------------------------------------------------------------- + +static void ShowExampleAppImageViewer(bool* p_open) +{ + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImTextureRef tex_ref = atlas->TexRef; // We don't have access to other textures in this demo! + int tex_w = atlas->TexData->Width; + int tex_h = atlas->TexData->Height; + if (ImGui::Begin("Example: Image Viewer", p_open)) + { + static ExampleImageViewerData image_viewer; + ExampleImageViewer_DrawOptions(&image_viewer); + ImVec2 canvas_size = ImGui::GetContentRegionAvail(); + ImVec2 canvas_min_size = ImGui::IsWindowAppearing() ? ImVec2(3.0f * tex_w, 4.0f * tex_h) : ImVec2(1.0f, 1.0f); + canvas_size = ImVec2(IM_MAX(canvas_size.x, canvas_min_size.x), IM_MAX(canvas_size.y, canvas_min_size.y)); + ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, tex_ref, tex_w, tex_h); + } + ImGui::End(); +} + //----------------------------------------------------------------------------- // [SECTION] Example App: Debug Log / ShowExampleAppLog() //----------------------------------------------------------------------------- From 02ccd9f34829f7ee575f6968f9fd772d7823ddb3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 May 2026 20:45:49 +0200 Subject: [PATCH 15/60] Scrolling, Inputs: using mouse wheel to scroll takes and keeps ownership of the corresponding keys while a wheeling window is locked. Ref 4448d975d. (#2604, #3795) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a27c45b3..e492452d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -111,6 +111,8 @@ Other Changes: - Detect and report error when calling End() instead of EndPopup() on a popup. (#9351) - Child windows with only ImGuiChildFlags_AutoResizeY flag keep using the proportional default ItemWidth. (#9355) + - Using mouse wheel to scroll takes and keeps ownership of the corresponding keys + (ImGuiKey_MouseWheelX/Y) while a wheeling window is locked. (#2604, #3795) - InputInt, InputFloat, InputScalar: reinstated ImGuiInputTextFlags_EnterReturnsTrue support which was removed in 1.91.4. (#8665, #9299, #8065, #3946, #6284, #9117) - Fixed the fact that it didn't return true when validating same value. diff --git a/imgui.cpp b/imgui.cpp index 20ccc021..1e752cdd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10364,15 +10364,21 @@ void ImGui::UpdateMouseWheel() LockWheelingWindow(NULL, 0.0f); } - ImVec2 wheel; - wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f; - wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f; - - //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; if (!mouse_window || mouse_window->Collapsed) return; + ImGuiID owner_id = mouse_window->ID; + ImVec2 wheel; + wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, owner_id) ? g.IO.MouseWheelH : 0.0f; + wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, owner_id) ? g.IO.MouseWheel : 0.0f; + //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); + if (g.WheelingWindow != NULL) + { + SetKeyOwner(ImGuiKey_MouseWheelX, owner_id); + SetKeyOwner(ImGuiKey_MouseWheelY, owner_id); + } + // Zoom / Scale window // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) From 0eae77f783beb2debfad701d5f9c77a0c1b44661 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 May 2026 20:51:32 +0200 Subject: [PATCH 16/60] Inputs: SetItemKeyOwner(): return true if ownership has been requested, which can to be checked to accurately gate further input test. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 13 ++++++++----- imgui.h | 5 +++-- imgui_demo.cpp | 6 +++--- imgui_internal.h | 2 +- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e492452d..e8f4776b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,10 @@ Other Changes: items straying out of columns boundaries. (#7994, #2221) - Box-Select + Tables: fixed an issue when calling `BeginMultiSelect()` in a table before layout has been locked (first row or headers row submitted). (#8250) +- Inputs: + - SetItemKeyOwner(): return true if ownership has been requested, which typically + needs to to checked for gating further tests. This is important as the function + may fail. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) - Style: - Fixed vertical scrollbar top coordinates when using thick borders on windows with no title bar and no menu bar. (#9366) diff --git a/imgui.cpp b/imgui.cpp index 1e752cdd..cd21c273 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10682,6 +10682,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) // - SetKeyOwner(..., None) : clears owner // - SetKeyOwner(..., Any, !Lock) : illegal (assert) // - SetKeyOwner(..., Any or None, Lock) : set lock +// Ownership is automatically released on the frame after a release, see code in UpdateKeyboardInputs(). void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; @@ -10708,30 +10709,32 @@ void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, I if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); } } -// This is more or less equivalent to: +// This is more or less equivalent to a fancier version of: // if (IsItemHovered() || IsItemActive()) // SetKeyOwner(key, GetItemID()); // Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times. // More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition. // Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority. -void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) +bool ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; ImGuiID id = g.LastItemData.ID; if (id == 0 || (g.HoveredId != id && g.ActiveId != id)) - return; + return false; if ((flags & ImGuiInputFlags_CondMask_) == 0) flags |= ImGuiInputFlags_CondDefault_; if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive))) { IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function! SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_); + return true; } + return false; } -void ImGui::SetItemKeyOwner(ImGuiKey key) +bool ImGui::SetItemKeyOwner(ImGuiKey key) { - SetItemKeyOwner(key, ImGuiInputFlags_None); + return SetItemKeyOwner(key, ImGuiInputFlags_None); } // This is the only public API until we expose owner_id versions of the API as replacements. diff --git a/imgui.h b/imgui.h index 376a008d..68e5a578 100644 --- a/imgui.h +++ b/imgui.h @@ -1092,10 +1092,11 @@ namespace ImGui // Inputs Utilities: Key/Input Ownership [BETA] // - One common use case would be to allow your items to disable standard inputs behaviors such // as Tab or Alt key handling, Mouse Wheel scrolling, etc. - // e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling. + // e.g. `Button(...); if (SetItemKeyOwner(ImGuiKey_MouseWheelY)) { ... }` to make hovering/activating a button disable wheel for scrolling. // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them. + // - The return value of SetItemKeyOwner() says if ownership has been requested for the item, which is a shortcut to calling yet non-public TestKeyOwner() function. // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version. - IMGUI_API void SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Return true when ownership has been set. Roughly equivalent to 'if (TestKeyOwner(key, GetItemID()) && (IsItemHovered() || IsItemActive())) { SetKeyOwner(key, GetItemID());'. // Inputs Utilities: Mouse // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d43d3495..39d9e1f0 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -875,9 +875,9 @@ static void ExampleImageViewer_DrawCanvas(ExampleImageViewerData* data, ImVec2 c data->ViewReset = false; // Handle inputs - ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY); // FIXME: Not while scrolling? - if (ImGui::IsItemHovered() && io.MouseWheel != 0.0f) - data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax); + if (ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY)) // FIXME: Not while scrolling? + if (io.MouseWheel != 0.0f) + data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax); float zoom = data->Zoom; // (float)(int)ViewZoom; if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) { diff --git a/imgui_internal.h b/imgui_internal.h index 918b6277..32fea290 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3436,7 +3436,7 @@ namespace ImGui IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } From 56b37bf93c3cbead5f2d4d4369848415a3b0a8b2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 7 May 2026 21:22:02 +0200 Subject: [PATCH 17/60] Inputs: SetItemKeyOwner(): does not set ownership is key is already taken. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 2 ++ imgui_demo.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e8f4776b..a35a7de0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -156,6 +156,10 @@ Other Changes: - SetItemKeyOwner(): return true if ownership has been requested, which typically needs to to checked for gating further tests. This is important as the function may fail. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) + - SetItemKeyOwner(): does not set ownership is key is already taken. Effectively + this makes using `SetItemKeyOwner(ImGuiKey_MouseWheelY)` over an item work as + expected while not having item react if a scroll wheel is actively in progress. + May be subject to further redesign, e.g. conditional flags. Feedback welcome! - Style: - Fixed vertical scrollbar top coordinates when using thick borders on windows with no title bar and no menu bar. (#9366) diff --git a/imgui.cpp b/imgui.cpp index cd21c273..8615cc0c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10726,6 +10726,8 @@ bool ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive))) { IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function! + if (!TestKeyOwner(key, id)) + return false; SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_); return true; } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 39d9e1f0..d251616e 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -875,7 +875,7 @@ static void ExampleImageViewer_DrawCanvas(ExampleImageViewerData* data, ImVec2 c data->ViewReset = false; // Handle inputs - if (ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY)) // FIXME: Not while scrolling? + if (ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY)) if (io.MouseWheel != 0.0f) data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax); float zoom = data->Zoom; // (float)(int)ViewZoom; From eb453f2be60f90fbeb8e0f952eb60b444c57608d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 14:30:34 +0200 Subject: [PATCH 18/60] Checkbox, Style: added ImGuiCol_CheckboxSelectedBg. (#9392) --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 1 + imgui.h | 3 ++- imgui_draw.cpp | 3 +++ imgui_widgets.cpp | 3 ++- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a35a7de0..d5ffccb4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -161,9 +161,11 @@ Other Changes: expected while not having item react if a scroll wheel is actively in progress. May be subject to further redesign, e.g. conditional flags. Feedback welcome! - Style: + - Checkbox: added ImGuiCol_CheckboxSelectedBg to change or accentuate the + background color of checked/mixed checkboxes. (#9392) + - Added ImGuiStyleVar_DragDropTargetRounding. (#9056) - Fixed vertical scrollbar top coordinates when using thick borders on windows with no title bar and no menu bar. (#9366) - - Added ImGuiStyleVar_DragDropTargetRounding. (#9056) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Assert when using MergeMode with an explicit size over a target font with an implicit diff --git a/imgui.cpp b/imgui.cpp index 8615cc0c..d07b5320 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3781,6 +3781,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; case ImGuiCol_CheckMark: return "CheckMark"; + case ImGuiCol_CheckboxSelectedBg: return "CheckboxSelectedBg"; case ImGuiCol_SliderGrab: return "SliderGrab"; case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; case ImGuiCol_Button: return "Button"; diff --git a/imgui.h b/imgui.h index 68e5a578..4a356009 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.8 WIP" -#define IMGUI_VERSION_NUM 19276 +#define IMGUI_VERSION_NUM 19277 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -1756,6 +1756,7 @@ enum ImGuiCol_ ImGuiCol_ScrollbarGrabHovered, ImGuiCol_ScrollbarGrabActive, ImGuiCol_CheckMark, // Checkbox tick and RadioButton circle + ImGuiCol_CheckboxSelectedBg, // Checkbox background when Selected, otherwise use FrameBg ImGuiCol_SliderGrab, ImGuiCol_SliderGrabActive, ImGuiCol_Button, diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 093f98a8..2e1e588e 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -208,6 +208,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_CheckboxSelectedBg] = ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBgHovered], 0.65f); colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); @@ -275,6 +276,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); + colors[ImGuiCol_CheckboxSelectedBg] = ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBgActive], 0.65f); colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); colors[ImGuiCol_Button] = ImVec4(0.35f, 0.40f, 0.61f, 0.62f); @@ -343,6 +345,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_CheckboxSelectedBg] = ImVec4(0.95f, 0.97f, 1.00f, 1.00f); colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.46f, 0.54f, 0.80f, 0.60f); colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b8fcd749..025076aa 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1288,8 +1288,9 @@ bool ImGui::Checkbox(const char* label, bool* v) if (is_visible) { RenderNavCursor(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : (mixed_value || checked) ? ImGuiCol_CheckboxSelectedBg : ImGuiCol_FrameBg); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); + RenderFrame(check_bb.Min, check_bb.Max, bg_col, true, style.FrameRounding); if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) From bca5a699282a4558820bbf611a7efb973a1b2a2c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 15:32:39 +0200 Subject: [PATCH 19/60] BeginMenu()/MenuItem(): fixed accidental triggering of child menu items when opening a menu inside a small host window forcing the child menu window to be repositioned under the mouse cursor. (#8233, #9394) nb: ImGuiSelectableFlags_NoHoldingActiveID is not used anymore. Would remove remove once we remove the unnecessary call to Selectable() from MenuItem(). --- docs/CHANGELOG.txt | 7 +++++++ imgui_widgets.cpp | 27 ++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d5ffccb4..da8b6fa4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,13 @@ Other Changes: items straying out of columns boundaries. (#7994, #2221) - Box-Select + Tables: fixed an issue when calling `BeginMultiSelect()` in a table before layout has been locked (first row or headers row submitted). (#8250) +- Menus: + - BeginMenu()/MenuItem(): fixed accidental triggering of child menu items when + opening a menu inside a small host window forcing the child menu window to be + repositioned under the mouse cursor. (#8233, #9394) + Done by reworking BeginMenu()/MenuItem(): they previously avoiding taking + ActiveID + key/click ownership (in order to allow releasing button on another item). + Now they take them and release them once the mouse is moved outside item boundaries. - Inputs: - SetItemKeyOwner(): return true if ownership has been requested, which typically needs to to checked for gating further tests. This is important as the function diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 025076aa..c7d7d8eb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9391,8 +9391,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; - // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -9430,6 +9429,14 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (!enabled) EndDisabled(); + // Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394) + // Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here. + if (g.ActiveId == id && g.HoveredId != id && g.ActiveIdSource == ImGuiInputSource_Mouse && IsMouseDragging(0)) + { + ClearActiveID(); + SetKeyOwner(ImGuiKey_MouseLeft, ImGuiKeyOwner_NoOwner); + } + const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav; if (menuset_is_open) PopItemFlag(); @@ -9510,6 +9517,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); PopID(); + if (g.ActiveId == id && want_open) + g.ActiveIdNoClearOnFocusLoss = true; + if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame. @@ -9602,7 +9612,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -9645,6 +9655,17 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } } + + // Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394) + // Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here. + const ImGuiID id = g.LastItemData.ID; + if (g.ActiveId == id && g.HoveredId != id && g.ActiveIdSource == ImGuiInputSource_Mouse && IsMouseDragging(0)) + { + ClearActiveID(); + SetKeyOwner(ImGuiKey_MouseLeft, ImGuiKeyOwner_NoOwner); + } + + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); if (!enabled) EndDisabled(); From b58836f2875e613d6b0fc5416ff7f30dcc2198e8 Mon Sep 17 00:00:00 2001 From: manuel Date: Fri, 1 May 2026 12:57:48 +0200 Subject: [PATCH 20/60] Backends: WebGPU: detect WGSL support at runtime instead of excluding WGVK at compile time. (#9387) Previously WGVK was hard-disabled from WGSL via #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK), forcing the SPIRV fallback unconditionally. Now the WGSL path is attempted on all backends and an empty stage_desc is returned when the module fails to compile letting the existing SPIRV fallback at the call site kick in. --- backends/imgui_impl_wgpu.cpp | 28 ++++++++++++++++++++++++---- docs/CHANGELOG.txt | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 5636178b..cd952cdc 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -328,11 +328,31 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleWGSL(con WGPUShaderModuleDescriptor desc = {}; desc.nextInChain = (WGPUChainedStruct*)&wgsl_desc; + // Detect shader compilation errors by using an error scope. + // Flag to be passed into the validation callback `userdata1` pointer. + int validation_error = 0; + wgpuDevicePushErrorScope(bd->wgpuDevice, WGPUErrorFilter_Validation); + WGPUShaderModule module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); + WGPUPopErrorScopeCallbackInfo pop_cb = {}; + pop_cb.mode = WGPUCallbackMode_AllowSpontaneous; + pop_cb.callback = [](WGPUPopErrorScopeStatus, WGPUErrorType type, WGPUStringView, void* userdata1, void*) + { + if (type == WGPUErrorType_Validation) + *static_cast(userdata1) = 1; + }; + pop_cb.userdata1 = &validation_error; + wgpuDevicePopErrorScope(bd->wgpuDevice, pop_cb); + WGPUProgrammableStageDescriptor stage_desc = {}; -#if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) - stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); - stage_desc.entryPoint = { "main", WGPU_STRLEN }; -#endif + if (module && !validation_error) + { + stage_desc.module = module; + stage_desc.entryPoint = { "main", WGPU_STRLEN }; + } + else if (module) + { + wgpuShaderModuleRelease(module); + } return stage_desc; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index da8b6fa4..45d3124e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -213,7 +213,7 @@ Other Changes: - SDL2: made `ImGui_ImplSDL2_GetContentScaleForWindow()`/`ImGui_ImplSDL2_GetContentScaleForDisplay()` helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369) - - WebGPU: always use SPIR-V shader on WGVK, as it cannot be detected at runtime. (#9316, #9246, #9257) + - WebGPU: rework choice/detection of using WGSL/SPIR-V shader on WGVK. (#9316, #9246, #9257, #9387) - Examples: - Update VS toolset in all .vcxproj from VS2015 (v140) to VS2017 (v141). The later is the first that supports vcpkg. Onward we will likely stop testing building the library with VS2015. From 2b31f651673453361d0a7be2837fadc800791362 Mon Sep 17 00:00:00 2001 From: manuel Date: Fri, 1 May 2026 12:57:48 +0200 Subject: [PATCH 21/60] Examples: WebGPU+GLFW/SDL2/SDL3: wire up the IMGUI_WGVK_DIR path. (#9387) The block was a stub - find Vulkan, detect Wayland/X11, set the SUPPORT_*_SURFACE defines that wgvk.c needs. --- examples/example_glfw_wgpu/CMakeLists.txt | 23 +++++++++++++++++++++++ examples/example_sdl2_wgpu/CMakeLists.txt | 23 +++++++++++++++++++++++ examples/example_sdl3_wgpu/CMakeLists.txt | 23 +++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt index 53bcda33..95d9fe38 100644 --- a/examples/example_glfw_wgpu/CMakeLists.txt +++ b/examples/example_glfw_wgpu/CMakeLists.txt @@ -146,6 +146,28 @@ else() # Native/Desktop build endif() if(IMGUI_WGVK_DIR) + find_package(Vulkan REQUIRED) + set(WGVK_PLATFORM_LIBS) + set(WGVK_PLATFORM_DEFS) + if(WIN32) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WIN32_SURFACE=1) + elseif(APPLE) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_METAL_SURFACE=1) + elseif(UNIX) + find_package(PkgConfig QUIET) + pkg_check_modules(WAYLAND_CLIENT QUIET wayland-client) + if(WAYLAND_CLIENT_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WAYLAND_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS ${WAYLAND_CLIENT_LIBRARIES}) + endif() + find_package(X11 QUIET) + if(X11_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_XLIB_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS X11::X11) + endif() + list(APPEND WGVK_PLATFORM_LIBS m dl pthread) + endif() + set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() @@ -187,6 +209,7 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(IMGUI_WGVK_DIR) target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_compile_definitions(${IMGUI_EXECUTABLE} PRIVATE ${WGVK_PLATFORM_DEFS}) target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) if (MSVC) target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) diff --git a/examples/example_sdl2_wgpu/CMakeLists.txt b/examples/example_sdl2_wgpu/CMakeLists.txt index 3bed79fd..aa8a1e57 100644 --- a/examples/example_sdl2_wgpu/CMakeLists.txt +++ b/examples/example_sdl2_wgpu/CMakeLists.txt @@ -141,6 +141,28 @@ else() # Native/Desktop build endif() if(IMGUI_WGVK_DIR) + find_package(Vulkan REQUIRED) + set(WGVK_PLATFORM_LIBS) + set(WGVK_PLATFORM_DEFS) + if(WIN32) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WIN32_SURFACE=1) + elseif(APPLE) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_METAL_SURFACE=1) + elseif(UNIX) + find_package(PkgConfig QUIET) + pkg_check_modules(WAYLAND_CLIENT QUIET wayland-client) + if(WAYLAND_CLIENT_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WAYLAND_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS ${WAYLAND_CLIENT_LIBRARIES}) + endif() + find_package(X11 QUIET) + if(X11_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_XLIB_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS X11::X11) + endif() + list(APPEND WGVK_PLATFORM_LIBS m dl pthread) + endif() + set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() @@ -183,6 +205,7 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(IMGUI_WGVK_DIR) target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_compile_definitions(${IMGUI_EXECUTABLE} PRIVATE ${WGVK_PLATFORM_DEFS}) target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) if (MSVC) target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) diff --git a/examples/example_sdl3_wgpu/CMakeLists.txt b/examples/example_sdl3_wgpu/CMakeLists.txt index 85de861e..7ecf0266 100644 --- a/examples/example_sdl3_wgpu/CMakeLists.txt +++ b/examples/example_sdl3_wgpu/CMakeLists.txt @@ -141,6 +141,28 @@ else() # Native/Desktop build endif() if(IMGUI_WGVK_DIR) + find_package(Vulkan REQUIRED) + set(WGVK_PLATFORM_LIBS) + set(WGVK_PLATFORM_DEFS) + if(WIN32) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WIN32_SURFACE=1) + elseif(APPLE) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_METAL_SURFACE=1) + elseif(UNIX) + find_package(PkgConfig QUIET) + pkg_check_modules(WAYLAND_CLIENT QUIET wayland-client) + if(WAYLAND_CLIENT_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WAYLAND_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS ${WAYLAND_CLIENT_LIBRARIES}) + endif() + find_package(X11 QUIET) + if(X11_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_XLIB_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS X11::X11) + endif() + list(APPEND WGVK_PLATFORM_LIBS m dl pthread) + endif() + set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() @@ -182,6 +204,7 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(IMGUI_WGVK_DIR) target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_compile_definitions(${IMGUI_EXECUTABLE} PRIVATE ${WGVK_PLATFORM_DEFS}) target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) if (MSVC) target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) From 821a39655678a039c2b8dca77a7845809592086b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 15:59:42 +0200 Subject: [PATCH 22/60] Fixed warning on Clang 26. --- imgui_widgets.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c7d7d8eb..47b7198f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9391,7 +9391,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoAutoClosePopups | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnClick; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -9612,7 +9612,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiSelectableFlags selectable_flags = (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnRelease | (ImGuiSelectableFlags)ImGuiSelectableFlags_SetNavIdOnHover; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { From ac1f57ba0c0c65f0bd89c997678f1c9863dafef9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 16:34:33 +0200 Subject: [PATCH 23/60] Fixed BeginMenu() leading to window with is_resizable_width=true. Fix/amend 95bd1577d. (#9355) We should instead probably make BeginChild() not set on ImGuiWindowFlags_AlwaysAutoResize on either ResizeX or ResizeY, but that'll be done later. --- imgui.cpp | 11 +++++++++++ imgui_demo.cpp | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d07b5320..15a91945 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12488,6 +12488,17 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext return false; } + // As we bypass BeginChild(), set ImGuiChildFlags_AlwaysAutoResize as it is checked independently from ImGuiWindowFlags_AlwaysAutoResize for now (see #9355) + // Ideally we should remove setting ImGuiWindowFlags_AlwaysAutoResize in BeginChild(). + if ((extra_window_flags & ImGuiWindowFlags_ChildWindow) || (extra_window_flags & ImGuiWindowFlags_AlwaysAutoResize)) + { + if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) + g.NextWindowData.ChildFlags |= ImGuiChildFlags_AlwaysAutoResize; + else + g.NextWindowData.ChildFlags = ImGuiChildFlags_AlwaysAutoResize; + g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags; + } + char name[128]; IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu); ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d251616e..28ad5b73 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -714,7 +714,7 @@ static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data) ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts); ImGui::EndDisabled(); ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert); - ImGui::TextDisabled("(see Demo->Configuration for details & more)"); + ImGui::TextDisabled("(see Demo->Configuration for more)"); ImGui::EndMenu(); } ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools); @@ -8903,7 +8903,7 @@ static void ShowExampleMenuFile() IMGUI_DEMO_MARKER("Examples/Menu/Options"); static bool enabled = true; ImGui::MenuItem("Enabled", "", &enabled); - ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders); + ImGui::BeginChild("child", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5.0f), ImGuiChildFlags_Borders); for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i); ImGui::EndChild(); From 4ac473b2c75583f1731df4fc2b235dc41e12c816 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 16:35:40 +0200 Subject: [PATCH 24/60] Fixed BeginMenu() leading to window with is_resizable_width=true. Fix/amend 95bd1577d + ac1f57b. (#9355) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 15a91945..fd23bc33 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12490,7 +12490,7 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext // As we bypass BeginChild(), set ImGuiChildFlags_AlwaysAutoResize as it is checked independently from ImGuiWindowFlags_AlwaysAutoResize for now (see #9355) // Ideally we should remove setting ImGuiWindowFlags_AlwaysAutoResize in BeginChild(). - if ((extra_window_flags & ImGuiWindowFlags_ChildWindow) || (extra_window_flags & ImGuiWindowFlags_AlwaysAutoResize)) + if ((extra_window_flags & ImGuiWindowFlags_ChildWindow) && (extra_window_flags & ImGuiWindowFlags_AlwaysAutoResize)) { if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) g.NextWindowData.ChildFlags |= ImGuiChildFlags_AlwaysAutoResize; From b8f6c51af7809a36492e4ba90d23abaa3a66f800 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 17:42:08 +0200 Subject: [PATCH 25/60] Fonts: move ClearFonts(), ClearInputData(), ClearTexData() implementation (no other changes) --- imgui_draw.cpp | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2e1e588e..c02ebd47 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2519,10 +2519,11 @@ void ImTextureData::DestroyPixels() // - Default texture data encoded in ASCII // - ImFontAtlas() // - ImFontAtlas::Clear() -// - ImFontAtlas::CompactCache() +// - ImFontAtlas::ClearFonts() // - ImFontAtlas::ClearInputData() // - ImFontAtlas::ClearTexData() -// - ImFontAtlas::ClearFonts() +// - ImFontAtlas::CompactCache() +// - ImFontAtlas::SetFontLoader() //----------------------------------------------------------------------------- // - ImFontAtlasUpdateNewFrame() // - ImFontAtlasTextureBlockConvert() @@ -2693,14 +2694,22 @@ void ImFontAtlas::Clear() RendererHasTextures = backup_renderer_has_textures; } -void ImFontAtlas::CompactCache() +void ImFontAtlas::ClearFonts() { - ImFontAtlasTextureCompact(this); -} - -void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) -{ - ImFontAtlasBuildSetupFontLoader(this, font_loader); + // FIXME-NEWATLAS: Illegal to remove currently bound font. + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); + for (ImFont* font : Fonts) + ImFontAtlasBuildNotifySetFont(this, font, NULL); + ImFontAtlasBuildDestroy(this); + ClearInputData(); + Fonts.clear_delete(); + TexIsBuilt = false; + for (ImDrawListSharedData* shared_data : DrawListSharedDatas) + if (shared_data->FontAtlas == this) + { + shared_data->Font = NULL; + shared_data->FontScale = shared_data->FontSize = 0.0f; + } } void ImFontAtlas::ClearInputData() @@ -2730,22 +2739,14 @@ void ImFontAtlas::ClearTexData() //Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it. } -void ImFontAtlas::ClearFonts() +void ImFontAtlas::CompactCache() { - // FIXME-NEWATLAS: Illegal to remove currently bound font. - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - for (ImFont* font : Fonts) - ImFontAtlasBuildNotifySetFont(this, font, NULL); - ImFontAtlasBuildDestroy(this); - ClearInputData(); - Fonts.clear_delete(); - TexIsBuilt = false; - for (ImDrawListSharedData* shared_data : DrawListSharedDatas) - if (shared_data->FontAtlas == this) - { - shared_data->Font = NULL; - shared_data->FontScale = shared_data->FontSize = 0.0f; - } + ImFontAtlasTextureCompact(this); +} + +void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) +{ + ImFontAtlasBuildSetupFontLoader(this, font_loader); } static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas) From 73afb6b6e61a19fa04615f8e8f8180626e0ea3cf Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 11 May 2026 17:45:20 +0200 Subject: [PATCH 26/60] Fonts: clarify that ClearFonts() be useful over calling Clear(). --- imgui.h | 5 +++-- imgui_draw.cpp | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/imgui.h b/imgui.h index 4a356009..00d959fc 100644 --- a/imgui.h +++ b/imgui.h @@ -3535,6 +3535,7 @@ struct ImTextureData bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions + // - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear() instead of ClearFonts()? ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); @@ -3690,13 +3691,13 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void RemoveFont(ImFont* font); - IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures). + IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame! + IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. You can call this mid-frame if you load new fonts afterwards! IMGUI_API void CompactCache(); // Compact cached glyphs and texture. IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime. // As we are transitioning toward a new font system, we expect to obsolete those soon: IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. - IMGUI_API void ClearFonts(); // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates). IMGUI_API void ClearTexData(); // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c02ebd47..ef1db7c1 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2684,7 +2684,9 @@ ImFontAtlas::~ImFontAtlas() TexData = NULL; } -// If you call this mid-frame, you would need to add new font and bind them! +// You probably should not call this directly. It is not well specified. +// If you want to replace all your fonts mid-frame, most likely you should instead call ClearFonts() then load the new fonts. +// Calling this mid-frame will discard the CPU-side copy of the texture data which is generally unreliable as you may have textures queued for creation or updates. void ImFontAtlas::Clear() { bool backup_renderer_has_textures = RendererHasTextures; @@ -2715,7 +2717,6 @@ void ImFontAtlas::ClearFonts() void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - for (ImFont* font : Fonts) ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig& font_cfg : Sources) From eebaddd3407ce18151fad189bf36c05e87e20602 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 11 May 2026 19:03:08 +0200 Subject: [PATCH 27/60] Docs: added SECURITY.md. --- docs/SECURITY.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/SECURITY.md diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 00000000..f2549efd --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,17 @@ +# Security Policy + +Dear ImGui is primarily designed for developer tools and technical applications running trusted code and trusted data. + +Given that the library was not designed as a hardened security boundary against hostile or intentionally malformed inputs: +consider not using it in a context where a crash (through e.g. intentionally malformed inputs) could lead to privilege elevation. +E.g. running in a privileged process and interacting with non-privileged clients feeding code or data to Dear ImGui. + +Dear ImGui development efforts are focused on improving developer experience, usability, correctness, and robustness in real-world applications. +We will do our best to reduce e.g. crashes caused by common programmer mistakes. + +However, the project does not specifically focus on adversarial fuzzing scenarios, allocation-failure hardening, or highly artificial edge cases that are unlikely to occur in normal usage. + +## Reporting a Vulnerability + +Considering the above policy, most things may be reported in GitHub Issues. +If you have a reason to disclose privately, please reach out to the contact address listed in README. From 8936b58fe26e8c3da834b8f60b06511d537b4c63 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 12 May 2026 15:48:41 +0200 Subject: [PATCH 28/60] Version 1.92.8 Include minor bits: adjust activeid logging, tweak comments. --- docs/CHANGELOG.txt | 108 +++++++++++++++++++++++---------------------- imgui.cpp | 27 ++++++------ imgui.h | 6 +-- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 4 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 8 ++-- 8 files changed, 84 insertions(+), 75 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 45d3124e..83f19a88 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,17 +36,14 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.92.8 WIP (In Progress) + VERSION 1.92.8 (Released 2026-05-12) ----------------------------------------------------------------------- +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.8 + Breaking Changes: -- DrawList: - - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, - which is part of our new standard draw callbacks. (#9378) - Redirecting the earlier value into the later one when set, so both old and new code should work. -- DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). - Recap: +- DrawList: swapped the last two arguments of `AddRect()`, `AddPolyline()`, `PathStroke()`. - Before: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);` - After: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0);` - Before: `void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);` @@ -55,36 +52,40 @@ Breaking Changes: - After: `void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0);` Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. - This is not an easy change but it makes ImDrawList function signatures consistent. - As we are aiming to add flags and features to variety of ImDrawList functions, that consistency will become particularly important. - The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. Effectively the typical call site is changing from: - Before: `window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size);` - After: `window->DrawList->AddRect(p_min, p_max, color, rounding, border_size);` Notes: - - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes. - Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value. + If you are a binding maintainer consider doing something to facilitate transition or error detection. + - This is perhaps the worst breaking change in our history :( but it makes ImDrawList function signatures consistent. + As we are aiming to add flags and features to variety of ImDrawList functions, that consistency becomes more important. + The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. + - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. +- DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, + which is part of our new standard draw callbacks. (#9378) + Redirecting the earlier value into the later one when set, so both old and new code should work. - Backends: - Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914) This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut] - When creating your own descriptor pool (instead of letting backend creates its own): - - Before: need at least IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER. - - After: need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. - + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. + - Before: need at least `IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER`. + - After: need at least `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`. + + `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLER`. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. - - Before: ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout) - - After: ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout) + - Before: `ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout)` + - After: `ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout)` - Kept inline redirection function that ignores the sampler (will obsolete). - - DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from ImGui_ImplXXXX_RenderState. + - DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from `ImGui_ImplXXXX_RenderState`. Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378) If there is a legit need/request for them or any render state we can always add them back. Other Changes: - DrawList: - - Added room in ImGuiPlatformIO for standard backend-agnostic draw callbacks. Those callbacks + - Added room in `ImGuiPlatformIO` for standard backend-agnostic draw callbacks. Those callbacks are setup/provided by the backend and available in most of our standard backends. They allow backend-agnostic code from e.g. switching to a Nearest/Point sampler without messing with custom Renderer-specific callbacks. @@ -94,13 +95,13 @@ Other Changes: Note that some backends might not support all callbacks. (#9378, #9371, #3590, #8926, #2973, #7485, #7468, #6969, #5118, #7616, #9173, #8322, #7230, #5999, #6452, #5156, #7342, #7592, #7511) - - Made AddCallback() user data default to Null for convenience. - - Added AddLineH(), AddLineV() helpers to draw horizontal and vertical lines. [@memononen] + - Made `AddCallback()` user data default to Null for convenience. + - Added `AddLineH()`, `AddLineV()` helpers to draw horizontal and vertical lines. [@memononen] - 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) - - CharFilter callback event sets CursorPos/SelectionStart/SelectionEnd. (#816) + - `CharFilter` callback event sets CursorPos/SelectionStart/SelectionEnd. (#816) - Tables: - 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. @@ -109,43 +110,45 @@ Other Changes: - 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) - - Child windows with only ImGuiChildFlags_AutoResizeY flag keep using the proportional - default ItemWidth. (#9355) + - Child windows with only `ImGuiChildFlags_AutoResizeY` flag keep using the proportional + default `ItemWidth`. (#9355) - Using mouse wheel to scroll takes and keeps ownership of the corresponding keys - (ImGuiKey_MouseWheelX/Y) while a wheeling window is locked. (#2604, #3795) -- InputInt, InputFloat, InputScalar: reinstated ImGuiInputTextFlags_EnterReturnsTrue - support which was removed in 1.91.4. (#8665, #9299, #8065, #3946, #6284, #9117) - - Fixed the fact that it didn't return true when validating same value. - - Fixed losing value when tabbing out or losing focus. - - Made it that pressing +/- step buttons also return true, which is in line - with 1.91.4 behavior. - - In a majority of cases you should use IsItemDeactivatedAfterEdit() instead, - but it still has a few edge cases flaws (to be addressed soon). -- InputInt, InputFloat, InputScalar: allow passing a format string that does not display - the scalar value. Parsing input with default format for the type. (#9385) [@FireFox2000000] + (e.g. `ImGuiKey_MouseWheelY`) while a wheeling window is locked. (#2604, #3795) +- InputInt, InputFloat, InputScalar: + - Reinstated `ImGuiInputTextFlags_EnterReturnsTrue` support which was removed in 1.91.4. + (#8665, #9299, #8065, #3946, #6284, #9117) + - Fixed the fact that it didn't return true when validating same value. + - Fixed losing value when tabbing out or losing focus. + - Made it that pressing +/- step buttons also return true, which is in line + with 1.91.4 behavior. + - In a majority of cases you should use `IsItemDeactivatedAfterEdit()` instead, + but it still has a few edge cases flaws (to be addressed soon). + - Allow passing a format string that does not display the scalar value. + Parsing input with default format for the type. (#9385) [@FireFox2000000] - Multi-Select: - Fixed an issue using Multi-Select within a Table causing column width measurement to be invalid when trailing column contents is not submitted in the last row. (#9341, #8250) - Fixed an issue using Multi-Select within a Table with the right-most column visible, which could lead to an extra vertical offset in the Header row. (#8250) - - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect1d mode while scrolling. +- Multi-Select + Box-Select: + - Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect1d` mode while scrolling. Notably, using mouse wheel while holding a box-selection could lead items close to windows edges from not being correctly unselected. (#7994, #8250, #7821, #7850, #7970) - - Box-Select: improved dirty/unclip rectangle logic for ImGuiMultiSelectFlags_BoxSelect2d. - - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect2d mode, where + - Improved dirty/unclip rectangle logic for `ImGuiMultiSelectFlags_BoxSelect2d`. + - Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect2d` mode, where items out of view wouldn't be properly selected while scrolling while mouse cursor is hovering outside of selection scope. (#7994, #1861, #6518) - - Box-Select: fixed an issue where items out of horizontal view would sometimes lead + - Fixed an issue where items out of horizontal view would sometimes lead to incorrect merging of sequential selection requests while also scrolling fast enough to overlap multiple rows during a frame. (#7994, #1861, #6518) - - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect2d mode in a Table - while relying on the TableNextColumn() return value to perform coarse clipping. (#7994) - - Box-Select: disabled merging consecutive selection requests as we have no reliable - way of detecting if user has submitted all consecutives items without clipping gaps, - and ImGuiSelectionUserData is technically opaque storage. (#7994, #1861) + - Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect2d` mode in a Table + while relying on the `TableNextColumn()` return value to perform coarse clipping. (#7994) + - Disabled merging consecutive selection requests as we have no reliable + way of detecting if user has submitted all consecutive items without clipping gaps, + and `ImGuiSelectionUserData` is technically opaque storage. (#7994, #1861) (we will probably bring this back as a minor optimization if we have a way to for - user to tell us ImGuiSelectionUserData are indices) - - Box-Select: fixes for using accross nested child windows. (#8364) + user to tell us `ImGuiSelectionUserData` are indices) + - Fixes for using across nested child windows. (#8364) - Box-Select + Clipper: fixed an issue selecting items while scrolling while a clipper active. (#7994, #8250, #7821, #7850, #7970) - Box-Select + Tables: fixed an issue using box-selection in a tables with @@ -156,7 +159,7 @@ Other Changes: - BeginMenu()/MenuItem(): fixed accidental triggering of child menu items when opening a menu inside a small host window forcing the child menu window to be repositioned under the mouse cursor. (#8233, #9394) - Done by reworking BeginMenu()/MenuItem(): they previously avoiding taking + Done by reworking `BeginMenu()`/`MenuItem()`: they previously avoiding taking ActiveID + key/click ownership (in order to allow releasing button on another item). Now they take them and release them once the mouse is moved outside item boundaries. - Inputs: @@ -168,9 +171,9 @@ Other Changes: expected while not having item react if a scroll wheel is actively in progress. May be subject to further redesign, e.g. conditional flags. Feedback welcome! - Style: - - Checkbox: added ImGuiCol_CheckboxSelectedBg to change or accentuate the + - Checkbox: added `ImGuiCol_CheckboxSelectedBg` to change or accentuate the background color of checked/mixed checkboxes. (#9392) - - Added ImGuiStyleVar_DragDropTargetRounding. (#9056) + - Added `ImGuiStyleVar_DragDropTargetRounding`. (#9056) - Fixed vertical scrollbar top coordinates when using thick borders on windows with no title bar and no menu bar. (#9366) - Fonts: @@ -183,8 +186,9 @@ Other Changes: - Tweaked assert triggering when first item height measurement fails, and made it a better recoverable error. (#9350) - Misc: - - Minor optimization: reduce redudant label scanning in common widgets. - - Added missing Test Engine hooks for PlotXXX(), VSliderXXX(), TableHeader(). + - Minor optimization: reduce redundant label scanning in common widgets. + - Added missing Test Engine hooks for `PlotLines()`, `PlotHistogram()`, `VSliderXXX()` + and `TableHeader()` functions. - Demo: - Added simple demo for a scrollable/zoomable image viewer with a grid. Available in `Examples->Image Viewer` and `Widgets->Images`. @@ -218,8 +222,8 @@ Other Changes: - Update VS toolset in all .vcxproj from VS2015 (v140) to VS2017 (v141). The later is the first that supports vcpkg. Onward we will likely stop testing building the library with VS2015. - GLFW+Vulkan, SDL2+Vulkan, SDL3+Vulkan, Win32+Vulkan: reworked to create a descriptor pool with: - - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. - - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. + - `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`. + - `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLER`. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index fd23bc33..2edb22be 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (main code and documentation) // Help: @@ -396,7 +396,6 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: You can read releases logs https://github.com/ocornut/imgui/releases for more details. - 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). - Recap: - Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); - After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); - Before: void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); @@ -405,18 +404,19 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - After: void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0); Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. - This is not an easy change but it makes ImDrawList function signatures consistent. - As we are aiming to add flags and features to variety of ImDrawList functions, that consistency will become particularly important. - The new order is also more convenient as 'flags' are less frequently used than 'thickness' in real code. Effectively the typical call site is changing from: - - Before: window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size); - - After: window->DrawList->AddRect(p_min, p_max, color, rounding, border_size); + - Before: window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size); + - After: window->DrawList->AddRect(p_min, p_max, color, rounding, border_size); Notes: - - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - - Users of C++ and other languages with type-checking should be notified at compile-time of any mistakes. + - Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes. - Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value. + If you are a binding maintainer consider doing something to facilitate transition or error detection. + - This is perhaps the worst breaking change in our history :( but it makes ImDrawList function signatures consistent. + As we are aiming to add flags and features to variety of ImDrawList functions, that consistency becomes more important. + The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. + - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. - - 2026/04/23 (1.92.8) - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) + - 2026/04/23 (1.92.8) - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) - 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. - When creating your own descriptor pool (instead of letting backend creates its own): need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. @@ -4720,7 +4720,8 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() 0x%08X in \"%s\"%*s(previously 0x%08X in \"%s\")\n", id, window ? window->Name : "", + ImMax(0, 20 - (int)(window ? strlen(window->Name) : 0)), "", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : ""); g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; @@ -5756,7 +5757,7 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags + g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); // Default flags g.CurrentItemFlags = g.ItemFlagsStack.back(); g.GroupStack.resize(0); @@ -18360,7 +18361,7 @@ void ImGui::ShowFontSelector(const char* label) "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n" "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" "- Read FAQ and docs/FONTS.md for more details.\n" - "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); + "- Legacy backend: if you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); } #endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS) diff --git a/imgui.h b/imgui.h index 00d959fc..9fdce397 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (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.8 WIP" -#define IMGUI_VERSION_NUM 19277 +#define IMGUI_VERSION "1.92.8" +#define IMGUI_VERSION_NUM 19280 #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 28ad5b73..7d882f80 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index ef1db7c1..cfea11c0 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 32fea290..d66b6a9d 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (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. @@ -1211,6 +1211,7 @@ struct IMGUI_API ImGuiMenuColumns }; // Internal temporary state for deactivating InputText() instances. +// Store as part of ImGuiDeactivatedItemData? struct IMGUI_API ImGuiInputTextDeactivatedState { ImGuiID ID; // widget id owning the text state (which just got deactivated) @@ -1456,6 +1457,7 @@ struct ImGuiPtrOrIndex }; // Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions +// Also see ImGuiInputTextDeactivatedState which is an extension for this for InputText() struct ImGuiDeactivatedItemData { ImGuiID ID; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 352ed62e..135410a0 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 47b7198f..ea792f9e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (widgets code) /* @@ -3572,7 +3572,8 @@ bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, // - ImParseFormatSanitizeForPrinting() [Internal] // - ImParseFormatSanitizeForScanning() [Internal] // - ImParseFormatPrecision() [Internal] -// - TempInputTextScalar() [Internal] +// - TempInputText() [Internal] +// - TempInputScalar() [Internal] // - InputScalar() // - InputScalarN() // - InputFloat() @@ -4583,6 +4584,7 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) ImGuiInputTextState* state = &g.InputTextState; if (id == 0 || state->ID != id) return; + //IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id); g.InputTextDeactivatedState.ID = state->ID; if (state->Flags & ImGuiInputTextFlags_ReadOnly) { @@ -4898,7 +4900,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } const bool is_osx = io.ConfigMacOSXBehaviors; - if (g.ActiveId != id && init_make_active) + if (init_make_active && g.ActiveId != id) { IM_ASSERT(state && state->ID == id); SetActiveID(id, window); From e0f5f9a14c4d11ea1c9fd1f7978a444333aa9993 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 12 May 2026 17:40:28 +0200 Subject: [PATCH 29/60] Amend AddRect(), AddPolyline() error detection to safely return an trigger error handling mechanism. Amend 6df50a0667a --- imgui.h | 2 +- imgui_draw.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.h b/imgui.h index 9fdce397..9a21e003 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.8" -#define IMGUI_VERSION_NUM 19280 +#define IMGUI_VERSION_NUM 19281 #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_draw.cpp b/imgui_draw.cpp index cfea11c0..4cc70951 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -823,7 +823,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > _FringeScale); - IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); + IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); if (Flags & ImDrawListFlags_AntiAliasedLines) { @@ -1510,7 +1510,7 @@ void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, fl // - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code. // - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'. // See "API BREAKING CHANGES" section for 1.82 and 1.90. - IM_ASSERT((flags & ImDrawFlags_InvalidMask_) == 0 && "Incorrect parameter. Did you swapped 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values + IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values if ((col & IM_COL32_A_MASK) == 0) return; From b2546a5c93090c70610046fe547b946ee3d8d986 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 13 May 2026 12:46:07 +0200 Subject: [PATCH 30/60] Comments on not needing to use ImDrawFlags_RoundCornersAll. --- docs/CHANGELOG.txt | 1 + imgui.h | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 83f19a88..e66a79ad 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,7 @@ Breaking Changes: The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. + - Reminder: you do NOT need to specify `ImDrawFlags_RoundCornersAll` for rounding, it is already the default! - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) Redirecting the earlier value into the later one when set, so both old and new code should work. diff --git a/imgui.h b/imgui.h index 9a21e003..a978ab73 100644 --- a/imgui.h +++ b/imgui.h @@ -3245,19 +3245,23 @@ struct ImDrawListSplitter enum ImDrawFlags_ { ImDrawFlags_None = 0, - ImDrawFlags_RoundCornersTopLeft = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01. - ImDrawFlags_RoundCornersTopRight = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02. - ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04. - ImDrawFlags_RoundCornersBottomRight = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08. - ImDrawFlags_RoundCornersNone = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag! - ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason) + + // Rounding for AddRect(), AddRectFilled(), PathRect() + // - When not specified, we defaults to ImDrawFlags_RoundCornersAll! So you only need to use those flags if you want another configuration. + ImDrawFlags_RoundCornersTopLeft = 1 << 4, // Round top-left corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersTopRight = 1 << 5, // Round top-right corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersBottomLeft = 1 << 6, // Round bottom-left corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersBottomRight = 1 << 7, // Round bottom-right corner only (when rounding > 0.0f, we default to all corners). + ImDrawFlags_RoundCornersNone = 1 << 8, // Disable rounding even if `float rounding > 0.0f`. This is NOT zero, NOT an implicit flag! + ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, // (Default!!) + ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified! ImDrawFlags_RoundCornersTop = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersBottom = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersLeft = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft, ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, - ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, - ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, + + ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed. ImDrawFlags_InvalidMask_ = (ImDrawFlags)0x8000000F, }; From 310c719a1fb6fce7c117f84aaa1075c2d9b20b35 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 10:48:24 +0200 Subject: [PATCH 31/60] Rework definition of ImDrawFlags_InvalidMask_ so it more strictly fits in an int32 for non C/C++ languages where it matters. (#9396, #9397) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index a978ab73..3b7cde0f 100644 --- a/imgui.h +++ b/imgui.h @@ -3262,7 +3262,7 @@ enum ImDrawFlags_ ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed. - ImDrawFlags_InvalidMask_ = (ImDrawFlags)0x8000000F, + ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // == 0x8000000F, }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. From c7767926ce2bc4b6520d43849ee66f4baff68454 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 11:31:46 +0200 Subject: [PATCH 32/60] Version 1.92.9 WIP --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 2 +- imgui.h | 4 ++-- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e66a79ad..e13e2c31 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.9 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +Other Changes: + + ----------------------------------------------------------------------- VERSION 1.92.8 (Released 2026-05-12) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 2edb22be..04eea7f2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 3b7cde0f..816642ed 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 WIP // (headers) // Help: @@ -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.92.8" +#define IMGUI_VERSION "1.92.9 WIP" #define IMGUI_VERSION_NUM 19281 #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 7d882f80..b95dd8a2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 4cc70951..d3041d5f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index d66b6a9d..b2d8c974 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 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 135410a0..98dd7baa 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ea792f9e..55b9e4cd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 +// dear imgui, v1.92.9 WIP // (widgets code) /* From 46a050fff2d9290803a1838ad64815e306eb89f0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 13:38:32 +0200 Subject: [PATCH 33/60] Nav: minor optimization NavUpdate(). To be honest this is mostly to ease debug stepping in the common case. --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 04eea7f2..01067abe 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13797,13 +13797,13 @@ static void ImGui::NavUpdate() // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource? const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown }; - if (nav_gamepad_active) + if (nav_gamepad_active && g.NavInputSource != ImGuiInputSource_Gamepad) for (ImGuiKey key : nav_gamepad_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Gamepad; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow }; - if (nav_keyboard_active) + if (nav_keyboard_active && g.NavInputSource != ImGuiInputSource_Keyboard) for (ImGuiKey key : nav_keyboard_keys_to_change_source) if (IsKeyDown(key)) g.NavInputSource = ImGuiInputSource_Keyboard; From 4088a4f40c25f143bddac3393a1f922426b379f5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 13:33:51 +0200 Subject: [PATCH 34/60] Windows: clicking on a window's empty-space to move/focus a window checks for lack of mouse button ownership. (#9382) --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e13e2c31..c69f33f3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,11 @@ Breaking Changes: Other Changes: +- Windows: + - Clicking on a window's empty-space to move/focus a window checks + for lack of mouse button ownership. This gives an additional opportunity + for user code to bypass it without using a clickable item. (#9382) + ----------------------------------------------------------------------- VERSION 1.92.8 (Released 2026-05-12) diff --git a/imgui.cpp b/imgui.cpp index 01067abe..a9677390 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5357,7 +5357,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Click on empty space to focus window and start moving // (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) + if (IsMouseClicked(0, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner)) { // Handle the edge case of a popup being closed while clicking in its empty space. // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. @@ -5395,7 +5395,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // With right mouse button we close popups without changing focus based on where the mouse is aimed // Instead, focus will be restored to the window under the bottom-most closed popup. // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1] && g.HoveredId == 0) + if (g.HoveredId == 0 && IsMouseClicked(1, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner)) { // Find the top-most window between HoveredWindow and the top-most Modal Window. // This is where we can trim the popup stack. From db161b84c9866ac29ea67608a45aa9385584a8eb Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 13:45:03 +0200 Subject: [PATCH 35/60] Windows: clicking on a window's empty-space to move/focus a window checks for lack of queued focus request. (#9382) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c69f33f3..abea19dc 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,8 @@ Other Changes: - Clicking on a window's empty-space to move/focus a window checks for lack of mouse button ownership. This gives an additional opportunity for user code to bypass it without using a clickable item. (#9382) + - Clicking on a window's empty-space to move/focus a window checks + for lack of queued focus request. (#9382) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index a9677390..c7557c8b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5363,8 +5363,9 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. ImGuiWindow* hovered_root = hovered_window ? hovered_window->RootWindow : NULL; const bool is_closed_popup = hovered_root && (hovered_root->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(hovered_root->PopupId, ImGuiPopupFlags_AnyPopupLevel); + const bool is_queued_focus_request = g.NavMoveSubmitted && (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi); - if (hovered_window != NULL && !is_closed_popup) + if (hovered_window != NULL && !is_closed_popup && !is_queued_focus_request) { StartMouseMovingWindow(hovered_window); //-V595 From 068e0555108d5fc93685f20ea6ee3ba599530d3d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 16:34:53 +0200 Subject: [PATCH 36/60] Textures: extract ImTextureDataUpdateNewFrame() out of ImFontAtlasUpdateNewFrame(). (#8465) --- imgui_draw.cpp | 83 ++++++++++++++++++++++++++---------------------- imgui_internal.h | 2 ++ 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d3041d5f..dce53339 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2817,48 +2817,12 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere // Update texture status for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++) { + // Update and remove if requested ImTextureData* tex = atlas->TexList[tex_n]; - bool remove_from_list = false; - if (tex->Status == ImTextureStatus_OK) - { - tex->Updates.resize(0); - tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0; - tex->UpdateRect.w = tex->UpdateRect.h = 0; - } if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures) IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK."); - // Request destroy - // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend. - // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM. - if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy) - { - IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates); - tex->Status = ImTextureStatus_WantDestroy; - } - - // If a texture has never reached the backend, they don't need to know about it. - // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy - // when invalidating graphics objects twice, which would previously remove it from the list and crash.) - if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL) - tex->Status = ImTextureStatus_Destroyed; - - // Process texture being destroyed - if (tex->Status == ImTextureStatus_Destroyed) - { - IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!"); - if (tex->WantDestroyNextFrame) - remove_from_list = true; // Destroy was scheduled by us - else - tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run) - } - - // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering. - // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision. - if (tex->Status == ImTextureStatus_WantDestroy) - tex->UnusedFrames++; - - // Destroy and remove + bool remove_from_list = ImTextureDataUpdateNewFrame(tex); if (remove_from_list) { IM_ASSERT(atlas->TexData != tex); @@ -2870,6 +2834,49 @@ void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool rendere } } +bool ImTextureDataUpdateNewFrame(ImTextureData* tex) +{ + bool remove_from_list = false; + if (tex->Status == ImTextureStatus_OK) + { + tex->Updates.resize(0); + tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0; + tex->UpdateRect.w = tex->UpdateRect.h = 0; + } + + // Request destroy + // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend. + // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM. + if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy) + { + IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates); + tex->Status = ImTextureStatus_WantDestroy; + } + + // If a texture has never reached the backend, they don't need to know about it. + // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy + // when invalidating graphics objects twice, which would previously remove it from the list and crash.) + if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL) + tex->Status = ImTextureStatus_Destroyed; + + // Process texture being destroyed + if (tex->Status == ImTextureStatus_Destroyed) + { + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!"); + if (tex->WantDestroyNextFrame) + remove_from_list = true; // Destroy was scheduled by us + else + tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run) + } + + // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering. + // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision. + if (tex->Status == ImTextureStatus_WantDestroy) + tex->UnusedFrames++; + + return remove_from_list; +} + void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h) { IM_ASSERT(src_pixels != NULL && dst_pixels != NULL); diff --git a/imgui_internal.h b/imgui_internal.h index b2d8c974..70f2bad3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1702,6 +1702,7 @@ enum ImGuiActivateFlags_ }; // Early work-in-progress API for ScrollToItem() +// FIXME: Missing flags to request making both edges visible when possible. enum ImGuiScrollFlags_ { ImGuiScrollFlags_None = 0, @@ -3979,6 +3980,7 @@ IMGUI_API void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, IMGUI_API void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h); IMGUI_API void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h); +IMGUI_API bool ImTextureDataUpdateNewFrame(ImTextureData* tex); IMGUI_API void ImTextureDataQueueUpload(ImTextureData* tex, int x, int y, int w, int h); IMGUI_API int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format); IMGUI_API const char* ImTextureDataGetStatusName(ImTextureStatus status); From 93e396ffb75834b4fa79004a4f0fccb600a8e164 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 16:38:05 +0200 Subject: [PATCH 37/60] Textures: call ImTextureDataUpdateNewFrame() for textures registered via RegisterUserTexture(). (#8789, #8465) Amend --- imgui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index c7557c8b..aa5fa6a1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8976,6 +8976,8 @@ static void ImGui::UpdateTexturesNewFrame() IM_ASSERT(atlas->RendererHasTextures == has_textures); } } + for (ImTextureData* tex : g.UserTextures) + ImTextureDataUpdateNewFrame(tex); } // Build a single texture list @@ -9037,7 +9039,7 @@ ImFont* ImGui::GetDefaultFont() return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0]; } -// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. +// EXPERIMENTAL. Use ImTextureDataQueueUpload() to queue updates. Textures logic will be automatically be updated in NewFrame(). void ImGui::RegisterUserTexture(ImTextureData* tex) { ImGuiContext& g = *GImGui; From e41d691da1aa3cd2c08bb56c058cf81c6ee31a2c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 18 May 2026 14:13:49 +0200 Subject: [PATCH 38/60] Demo: Tree Nodes: extract 'Tree Nodes->Selectable Nodes' into its own thing. + comments (#9401) --- docs/CHANGELOG.txt | 3 ++ imgui.cpp | 11 ++--- imgui.h | 2 +- imgui_demo.cpp | 107 ++++++++++++++++++++++++++------------------- 4 files changed, 72 insertions(+), 51 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index abea19dc..d5d77bee 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,9 @@ Other Changes: for user code to bypass it without using a clickable item. (#9382) - Clicking on a window's empty-space to move/focus a window checks for lack of queued focus request. (#9382) +- Demo: + - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' + demo for clarity (manual reimplementation of basic selection). ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index aa5fa6a1..c0293949 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6318,11 +6318,12 @@ bool ImGui::IsItemToggledOpen() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false; } -// Call after a Selectable() or TreeNode() involved in multi-selection. -// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose. -// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block. -// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets -// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.) +// Call after a Selectable() or TreeNode() items inside a BeginMultiSelect()/EndMultiSelect() scope. +// - Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose. +// Outside of a multi-select block: +// - It would be misleading/ambiguous to report this signal, as widgets return e.g. a pressed event, +// and user code is in charge of altering selection in ways we cannot predict. +// Prefer using 'if (IsItemClicked() && !IsItemToggledOpen())' for a manual reimplementation of selection. bool ImGui::IsItemToggledSelection() { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index 816642ed..b6763d42 100644 --- a/imgui.h +++ b/imgui.h @@ -837,7 +837,7 @@ namespace ImGui // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. - // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. + // - If not modal: they can be closed by clicking anywhere outside them, or by pressing Escape (call 'Shortcut(ImGuiKey_Escape)' to claim a higher-priority shortcut). // - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls. // - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time. // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b95dd8a2..673a0248 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4138,9 +4138,9 @@ static void DemoWindowWidgetsTreeNodes() { IMGUI_DEMO_MARKER("Widgets/Tree Nodes"); // See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree. - if (ImGui::TreeNode("Basic trees")) + if (ImGui::TreeNode("Basic Trees")) { - IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic Trees"); for (int i = 0; i < 5; i++) { // Use SetNextItemOpen() so set the default state of a node to be open. We could @@ -4164,9 +4164,9 @@ static void DemoWindowWidgetsTreeNodes() ImGui::TreePop(); } - if (ImGui::TreeNode("Hierarchy lines")) + if (ImGui::TreeNode("Hierarchy Lines")) { - IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy Lines"); static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen; HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags"); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone); @@ -4203,15 +4203,57 @@ static void DemoWindowWidgetsTreeNodes() ImGui::TreePop(); } - if (ImGui::TreeNode("Advanced, with Selectable nodes")) + if (ImGui::TreeNode("Selectable Nodes")) { - IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Selectable Nodes"); HelpMarker( - "This is a more typical looking tree with selectable nodes.\n" - "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open."); + "Manually implemented selectable nodes.\n" + "Click to select, Ctrl+Click to toggle, click on arrows or double-click to open.\n\n" + "You may also use the multi-select API (see 'Demo->Widgets->Selection State & Multi-Select') for more advanced multi-selection features."); + + // Hold in 'selection_mask' a simple representation of what may be user-side selection state. + // - You may retain selection state inside or outside your objects in whatever format you see fit. + // You may use ImGuiSelectionBasicStorage which is conceptually close to a set<> of identifiers. + // - We record which node was clicked and then apply selection at the end of the loop. + // - This is a manual and simplified reimplementation of multi-selection, which the full + // BeginMultiSelect() API implements better, but which is not trivial to wire for trees. + static int selection_mask = 0x00; + int node_clicked_idx = -1; + for (int node_n = 0; node_n < 6; node_n++) + { + // Disable the default "open on single-click behavior" + set Selected flag according to our selection. + // To alter selection we use if 'IsItemClicked() && !IsItemToggledOpen()', so clicking on an arrow doesn't alter selection. + // In a BeginMultiSelect()/EndMultiSelect() we could use IsItemToggledSelection() but here we reimplement and use our own logic. + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; + if (selection_mask & (1 << node_n)) + flags |= ImGuiTreeNodeFlags_Selected; + + bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, flags, "Selectable Node %d", node_n); + if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) + node_clicked_idx = node_n; + if (is_open) + { + ImGui::BulletText(""); + ImGui::TreePop(); + } + } + if (node_clicked_idx != -1) + { + // Update selection state (process outside of tree loop to avoid visual inconsistencies during the clicking frame) + if (ImGui::GetIO().KeyCtrl) + selection_mask ^= (1 << node_clicked_idx); // Ctrl+Click to toggle + else //if (!(selection_mask & (1 << node_clicked_idx))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection + selection_mask = (1 << node_clicked_idx); // Click to single-select + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Advanced")) + { + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced"); static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; static bool align_label_with_current_x_position = false; - static bool test_drag_and_drop = false; + static bool use_drag_and_drop = false; ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); @@ -4229,44 +4271,30 @@ static void DemoWindowWidgetsTreeNodes() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes); ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); - ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); - ImGui::Text("Hello!"); + ImGui::Checkbox("Make Tree Nodes as drag & drop sources", &use_drag_and_drop); if (align_label_with_current_x_position) ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - // 'selection_mask' is dumb representation of what may be user-side selection state. - // You may retain selection state inside or outside your objects in whatever format you see fit. - // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end - /// of the loop. May be a pointer to your own node type, etc. - static int selection_mask = (1 << 2); - int node_clicked = -1; - for (int i = 0; i < 6; i++) + for (int node_n = 0; node_n < 6; node_n++) { - // Disable the default "open on single-click behavior" + set Selected flag according to our selection. - // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection. ImGuiTreeNodeFlags node_flags = base_flags; - const bool is_selected = (selection_mask & (1 << i)) != 0; - if (is_selected) - node_flags |= ImGuiTreeNodeFlags_Selected; - if (i < 3) + if (node_n < 3) { // Items 0..2 are Tree Node - bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) - node_clicked = i; - if (test_drag_and_drop && ImGui::BeginDragDropSource()) + bool is_open = ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Node %d", node_n); + if (use_drag_and_drop && ImGui::BeginDragDropSource()) { - ImGui::SetDragDropPayload("_TREENODE", NULL, 0); + ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0); ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } - if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth)) + if (node_n == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth)) { // Item 2 has an additional inline button to help demonstrate SpanLabelWidth. ImGui::SameLine(); if (ImGui::SmallButton("button")) {} } - if (node_open) + if (is_open) { ImGui::BulletText("Blah blah\nBlah Blah"); ImGui::SameLine(); @@ -4280,26 +4308,15 @@ static void DemoWindowWidgetsTreeNodes() // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text(). node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet - ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) - node_clicked = i; - if (test_drag_and_drop && ImGui::BeginDragDropSource()) + ImGui::TreeNodeEx((void*)(intptr_t)node_n, node_flags, "Selectable Leaf %d", node_n); + if (use_drag_and_drop && ImGui::BeginDragDropSource()) { - ImGui::SetDragDropPayload("_TREENODE", NULL, 0); + ImGui::SetDragDropPayload("MY_TREENODE_PAYLOAD_TYPE", NULL, 0); ImGui::Text("This is a drag and drop source"); ImGui::EndDragDropSource(); } } } - if (node_clicked != -1) - { - // Update selection state - // (process outside of tree loop to avoid visual inconsistencies during the clicking frame) - if (ImGui::GetIO().KeyCtrl) - selection_mask ^= (1 << node_clicked); // Ctrl+Click to toggle - else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection - selection_mask = (1 << node_clicked); // Click to single-select - } if (align_label_with_current_x_position) ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::TreePop(); From 3e7b79aa177c494481a9bfed27b530f2ec84a74b Mon Sep 17 00:00:00 2001 From: MouriNaruto Date: Tue, 19 May 2026 23:21:36 +0800 Subject: [PATCH 39/60] Backends: Win32: use SetProcessDpiAwarenessContext instead of SetThreadDpiAwarenessContext when available, to fix the OpenGL3 Win32 example DPI scaling issue. (#9403) --- backends/imgui_impl_win32.cpp | 9 ++++++++- docs/CHANGELOG.txt | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index d789f25f..787329bf 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-05-19: DPI: ImGui_ImplWin32_EnableDpiAwareness() helper uses SetProcessDpiAwarenessContext() instead of SetThreadDpiAwarenessContext(), fixes DPI scaling issues with e.g. OpenGL. (#9403) // 2026-01-28: Inputs: Minor optimization not submitting gamepad input if packet number has not changed (reworked from 2025-09-23 attempt). (#9202, #8556) // 2025-12-03: Inputs: handle WM_IME_CHAR/WM_IME_COMPOSITION messages to support Unicode inputs on MBCS (non-Unicode) Windows. (#9099, #3653, #5961) // 2025-10-19: Inputs: Revert previous change to allow for io.ClearInputKeys() on focus-out not losing gamepad state. @@ -886,6 +887,7 @@ DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); typedef HRESULT(WINAPI* PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); // Shcore.lib + dll, Windows 8.1+ typedef HRESULT(WINAPI* PFN_GetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); // Shcore.lib + dll, Windows 8.1+ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1607+ (Creators Update) +typedef BOOL(WINAPI* PFN_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); // User32.lib + dll, Windows 10 v1703+ (Creators Update) // Helper function to enable DPI awareness without setting up a manifest void ImGui_ImplWin32_EnableDpiAwareness() @@ -893,7 +895,12 @@ void ImGui_ImplWin32_EnableDpiAwareness() if (_IsWindows10OrGreater()) { static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process - if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) + if (PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContextFn = (PFN_SetProcessDpiAwarenessContext)::GetProcAddress(user32_dll, "SetProcessDpiAwarenessContext")) // Windows 10 v1703+ + { + SetProcessDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + return; + } + if (PFN_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContextFn = (PFN_SetThreadDpiAwarenessContext)::GetProcAddress(user32_dll, "SetThreadDpiAwarenessContext")) // Windows 10 v1607+ { SetThreadDpiAwarenessContextFn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); return; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d5d77bee..59ad650b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,11 @@ Other Changes: - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). +- Backends: + - Win32: + - Uses `SetProcessDpiAwarenessContext()` instead of `SetThreadDpiAwarenessContext()` + when available, fixing OpenGL DPI scaling issues as e.g. NVIDIA drivers tends + to spawn multiple-thread to manage OpenGL. (#9403) ----------------------------------------------------------------------- From 904b6631845d74ab1c19cb47b743c1a7122426dc Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 19 May 2026 18:50:39 +0200 Subject: [PATCH 40/60] Clarify support for "%s" shortcuts in functions taking format strings. (#9404, #3466, #6846) --- imgui.h | 3 ++- imgui_widgets.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index b6763d42..78741766 100644 --- a/imgui.h +++ b/imgui.h @@ -613,7 +613,8 @@ namespace ImGui IMGUI_API ImGuiID GetID(int int_id); // Widgets: Text - IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. + // - Note that all functions taking format strings in the API may be passed ("%s", text) or ("%.*s", text_len, text): which will automatically bypass the formatter. + IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Practically equivalent to 'Text("%s", text)' but doesn't require null terminated string if 'text_end' is specified. IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // formatted text IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 55b9e4cd..63e3f5e0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -266,6 +266,8 @@ void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags) } } +// Note that all functions taking format strings in the API may be passed ("%s", text) or ("%.*s", text_len, text), +// which will automatically bypass the formatter. void ImGui::TextUnformatted(const char* text, const char* text_end) { TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText); From 24a80f74a4ec64c5bfd5bee67ac567466ffcc9a4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 May 2026 19:11:33 +0200 Subject: [PATCH 41/60] InputText, Style: added InputTextCursorSize to configure cursor/caret thickness. (#7031, #9409) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 ++ imgui.h | 1 + imgui_widgets.cpp | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 59ad650b..ce0f751a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -49,6 +49,9 @@ Other Changes: for user code to bypass it without using a clickable item. (#9382) - Clicking on a window's empty-space to move/focus a window checks for lack of queued focus request. (#9382) +- InputText: + - Added style.InputTextCursorSize to configure cursor/caret thickness. (#7031, #9409) + This is automatically scaled by style.ScaleAllSizes(). - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). diff --git a/imgui.cpp b/imgui.cpp index c0293949..25e28ed1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1537,6 +1537,7 @@ ImGuiStyle::ImGuiStyle() ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. + InputTextCursorSize = 1.0f; // Thickness of cursor/caret in InputText(). SeparatorSize = 1.0f; // Thickness of border in Separator(). SeparatorTextBorderSize = 3.0f; // Thickness of border in SeparatorText(). SeparatorTextAlign = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). @@ -1612,6 +1613,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) DragDropTargetBorderSize = ImTrunc(DragDropTargetBorderSize * scale_factor); DragDropTargetPadding = ImTrunc(DragDropTargetPadding * scale_factor); ColorMarkerSize = ImTrunc(ColorMarkerSize * scale_factor); + InputTextCursorSize = ImTrunc(InputTextCursorSize * scale_factor); SeparatorSize = ImTrunc(SeparatorSize * scale_factor); SeparatorTextBorderSize = ImTrunc(SeparatorTextBorderSize * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); diff --git a/imgui.h b/imgui.h index 78741766..1c697eb3 100644 --- a/imgui.h +++ b/imgui.h @@ -2339,6 +2339,7 @@ struct ImGuiStyle ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. + float InputTextCursorSize; // Thickness of cursor/caret in InputText(). float SeparatorSize; // Thickness of border in Separator(). Must be >= 1.0f. float SeparatorTextBorderSize; // Thickness of border in SeparatorText() ImVec2 SeparatorTextAlign; // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 63e3f5e0..3807f1f0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5643,7 +5643,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), 1.0f * (float)(int)style._MainScale); // FIXME-DPI: Cursor thickness (#7031) + draw_window->DrawList->AddLineV(cursor_screen_rect.Min.x, cursor_screen_rect.Min.y, cursor_screen_rect.Max.y, GetColorU32(ImGuiCol_InputTextCursor), style.InputTextCursorSize); // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) // This is required for some backends (SDL3) to start emitting character/text inputs. From fbcf95193f40fc57a3f0a3e8f59798de06e69bc6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 22 May 2026 20:03:30 +0200 Subject: [PATCH 42/60] ImFontAtlas: moved common TexData calls into a same helper functions, so adding new ones is easier. --- imgui_draw.cpp | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index dce53339..c695751a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2560,8 +2560,6 @@ void ImTextureData::DestroyPixels() // - ImFontAtlasBuildPreloadAllGlyphRanges() // - ImFontAtlasBuildUpdatePointers() // - ImFontAtlasBuildRenderBitmapFromString() -// - ImFontAtlasBuildUpdateBasicTexData() -// - ImFontAtlasBuildUpdateLinesTexData() // - ImFontAtlasBuildAddFont() // - ImFontAtlasBuildSetupFontBakedEllipsis() // - ImFontAtlasBuildSetupFontBakedBlanks() @@ -2578,13 +2576,14 @@ void ImTextureData::DestroyPixels() // - ImFontAtlasUpdateDrawListsSharedData() //----------------------------------------------------------------------------- // - ImFontAtlasBuildSetTexture() -// - ImFontAtlasBuildAddTexture() -// - ImFontAtlasBuildMakeSpace() -// - ImFontAtlasBuildRepackTexture() -// - ImFontAtlasBuildGrowTexture() -// - ImFontAtlasBuildRepackOrGrowTexture() -// - ImFontAtlasBuildGetTextureSizeEstimate() -// - ImFontAtlasBuildCompactTexture() +// - ImFontAtlasBuildUpdateTexData() +// - ImFontAtlasTextureAdd() +// - ImFontAtlasTextureRepack() +// - ImFontAtlasTextureGrow() +// - ImFontAtlasTextureMakeSpace() +// - ImFontAtlasTextureGetSizeEstimate() +// - ImFontAtlasBuildClear() +// - ImFontAtlasTextureCompact() // - ImFontAtlasBuildInit() // - ImFontAtlasBuildDestroy() //----------------------------------------------------------------------------- @@ -3581,7 +3580,7 @@ void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, in } } -static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas) +static void ImFontAtlasBuildUpdateTexDataBasic(ImFontAtlas* atlas) { // Pack and store identifier so we can refresh UV coordinates on texture resize. // FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing. @@ -3615,7 +3614,7 @@ static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas) atlas->TexUvWhitePixel = ImVec2((r.x + 0.5f) * atlas->TexUvScale.x, (r.y + 0.5f) * atlas->TexUvScale.y); } -static void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas) +static void ImFontAtlasBuildUpdateTexDataLines(ImFontAtlas* atlas) { if (atlas->Flags & ImFontAtlasFlags_NoBakedLines) return; @@ -4064,6 +4063,12 @@ static void ImFontAtlasBuildSetTexture(ImFontAtlas* atlas, ImTextureData* tex) ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas->TexRef); } +static void ImFontAtlasBuildUpdateTexData(ImFontAtlas* atlas) +{ + ImFontAtlasBuildUpdateTexDataBasic(atlas); + ImFontAtlasBuildUpdateTexDataLines(atlas); +} + // Create a new texture, discard previous one ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h) { @@ -4178,8 +4183,7 @@ void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h) } // Update other cached UV - ImFontAtlasBuildUpdateLinesTexData(atlas); - ImFontAtlasBuildUpdateBasicTexData(atlas); + ImFontAtlasBuildUpdateTexData(atlas); builder->LockDisableResize = false; ImFontAtlasUpdateDrawListsSharedData(atlas); @@ -4328,8 +4332,7 @@ void ImFontAtlasBuildInit(ImFontAtlas* atlas) ImFontAtlasPackInit(atlas); // Add required texture data - ImFontAtlasBuildUpdateLinesTexData(atlas); - ImFontAtlasBuildUpdateBasicTexData(atlas); + ImFontAtlasBuildUpdateTexData(atlas); // Register fonts ImFontAtlasBuildUpdatePointers(atlas); @@ -4358,6 +4361,8 @@ void ImFontAtlasBuildDestroy(ImFontAtlas* atlas) atlas->Builder = NULL; } +//----------------------------------------------------------------------------- + void ImFontAtlasPackInit(ImFontAtlas * atlas) { ImTextureData* tex = atlas->TexData; From 243097ca8f46ffe361b7aca642606c3ad176c9d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 26 May 2026 20:36:29 +0200 Subject: [PATCH 43/60] Docs: retroactively amend changelog for AddLineH(), AddLineV(). Amend 691b89b. (#9360) --- docs/CHANGELOG.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ce0f751a..091620ec 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -125,6 +125,15 @@ Other Changes: #5999, #6452, #5156, #7342, #7592, #7511) - Made `AddCallback()` user data default to Null for convenience. - Added `AddLineH()`, `AddLineV()` helpers to draw horizontal and vertical lines. [@memononen] + The new functions are more optimal and will be part of a larger effort in 1.93 to fix + inconsistencies and improve the DrawList API. In the current API, `AddLine()` adds a "magic" +0.5f + offset and then center the line, whereas AddLineH()/AddLineV() use the right side of the line to expand. + For thickness=1.0f lines this is equivalent: + `AddLine({10,3}, {20,3}, ...)` --> `AddLineH(x1=10, x2=20, y=3, ...)`. + `AddLine({3,10}, {3,20}, ...)` --> `AddLineV(x=3, y1=10, y2=20, ...)`. + For larger integer thickness, AddLine() will center but may be blurry, AddLineH()/AddLineV() + will expand "inside" (to the right side) and will always be sharp. + A subsequent release will expand on those concepts and add new options. - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. From ac07da2b5bf3c0e1b1e7ede1a8ab2e0abf6fc52a Mon Sep 17 00:00:00 2001 From: qwer <58898485+khuiqel@users.noreply.github.com> Date: Wed, 20 May 2026 21:07:56 -0700 Subject: [PATCH 44/60] Fonts: Added macros to disable ProggyClean/ProggyVector separately. (#9407) --- docs/CHANGELOG.txt | 3 +++ imconfig.h | 4 +++- imgui_draw.cpp | 20 ++++++++++++-------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 091620ec..0cfd5458 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,9 @@ Other Changes: - InputText: - Added style.InputTextCursorSize to configure cursor/caret thickness. (#7031, #9409) This is automatically scaled by style.ScaleAllSizes(). +- Fonts: + - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to + disable embedding either fonts separately. (#9407) - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). diff --git a/imconfig.h b/imconfig.h index 0d843be2..0a4ca08a 100644 --- a/imconfig.h +++ b/imconfig.h @@ -49,7 +49,9 @@ //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). -//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean/ProggyForever), remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded fonts (ProggyClean + ProggyForever). Remove ~9 KB + ~14 KB from output binary. AddFontDefaultXXX() functions will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT_BITMAP // Disable default embedded bitmap font (ProggyClean). Remove ~9 KB from output binary. AddFontDefaultBitmap() will assert. +//#define IMGUI_DISABLE_DEFAULT_FONT_VECTOR // Disable default embedded vector font (ProggyForever), Remove ~14 KB from output binary. AddFontDefaultVector() will assert. //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Enable Test Engine / Automation features. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c695751a..6cee3f46 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3149,8 +3149,10 @@ static void Decode85(const unsigned char* src, unsigned char* dst) dst += 4; } } -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) static const char* GetDefaultCompressedFontDataProggyClean(int* out_size); +#endif +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) static const char* GetDefaultCompressedFontDataProggyForever(int* out_size); #endif @@ -3175,7 +3177,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg) // If you want a similar font which may be better scaled, consider using AddFontDefaultVector(). ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) { -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) font_cfg.PixelSnapH = true; // Prevents sub-integer scaling factors at lower-level layers. @@ -3196,14 +3198,14 @@ ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +#endif } // Load a minimal version of ProggyForever, designed to match our good old ProggyClean, but nicely scalable. // (See build script in https://github.com/ocornut/proggyforever for details) ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) { -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); if (!font_cfg_template) font_cfg.PixelSnapH = true; // Precisely match ProggyClean, but prevents sub-integer scaling factors at lower-level layers. @@ -3224,7 +3226,7 @@ ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) IM_ASSERT(0 && "Function is disabled in this build."); IM_UNUSED(font_cfg_template); return NULL; -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +#endif } ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) @@ -6340,7 +6342,7 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i // Download and more information at https://github.com/bluescan/proggyfonts //----------------------------------------------------------------------------- -#ifndef IMGUI_DISABLE_DEFAULT_FONT +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) // File: 'ProggyClean.ttf' (41208 bytes) // Exported using binary_to_compressed_c.exe -u8 "ProggyClean.ttf" proggy_clean_ttf @@ -6520,6 +6522,7 @@ static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) *out_size = proggy_clean_ttf_compressed_size; return (const char*)proggy_clean_ttf_compressed_data; } +#endif // #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) //----------------------------------------------------------------------------- // [SECTION] Default font data (ProggyForever-Regular-minimal.ttf) @@ -6528,6 +6531,8 @@ static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) // MIT license / Copyright (c) 2026 Disco Hello, Copyright (c) 2019,2023 Tristan Grimmer //----------------------------------------------------------------------------- +#if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) + // File: 'output/ProggyForever-Regular-minimal.ttf' (18556 bytes) // Exported using binary_to_compressed_c.exe -u8 "output/ProggyForever-Regular-minimal.ttf" proggy_forever_minimal_ttf static const unsigned int proggy_forever_minimal_ttf_compressed_size = 14562; @@ -6782,7 +6787,6 @@ static const char* GetDefaultCompressedFontDataProggyForever(int* out_size) *out_size = proggy_forever_minimal_ttf_compressed_size; return (const char*)proggy_forever_minimal_ttf_compressed_data; } - -#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT +#endif // #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) #endif // #ifndef IMGUI_DISABLE From 783eba926fc064b99f917020b01596bd0e201202 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 28 May 2026 11:37:37 +0200 Subject: [PATCH 45/60] Docs: retroactively amend 1.92.8 changelog about `ImDrawFlags_Closed` value. Amend 6df50a0 --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 ++ imgui.h | 3 +++ imgui_draw.cpp | 10 ++++++++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0cfd5458..17aadb6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,6 +98,9 @@ Breaking Changes: - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) Redirecting the earlier value into the later one when set, so both old and new code should work. +- DrawList: changed value of `ImDrawFlags_Closed`. It was previously advertised as "always == 1" when introduced + in 1.82 (2021/02), in order to facilitate backward compatibility with the legacy `bool closed` flag. This + guarantee has been removed. The bit is reserved, and `AddPolyline()`, `PathStroke()` will assert when it is used. - Backends: - Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914) This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut] diff --git a/imgui.cpp b/imgui.cpp index 25e28ed1..ecd3bc1c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -416,6 +416,8 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. + - 2026/05/07 (1.92.8) - DrawList: changed value of `ImDrawFlags_Closed`. It was previously advertised as "always == 1" when introduced in 1.82 (2021/02), in order to facilitate backward compatibility with the legacy `bool closed` flag. + This guarantee has been removed. The bit is reserved and `AddPolyline()`, `PathStroke()` will assert when it is used. - 2026/04/23 (1.92.8) - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) - 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. diff --git a/imgui.h b/imgui.h index 1c697eb3..cc80fc80 100644 --- a/imgui.h +++ b/imgui.h @@ -3263,7 +3263,10 @@ enum ImDrawFlags_ ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, + // Stroke options ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed. + //ImDrawFlags_Closed = 1 << 0, // Prior to 1.92.8 (May 2026), ImDrawFlags_Closed was guaranteed to be == 1<<0 == 1 for legacy compatibility reason. Hardcoded use of 1 or true should be replaced. + ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // == 0x8000000F, }; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6cee3f46..690b7e5b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -823,6 +823,11 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 const ImVec2 opaque_uv = _Data->TexUvWhitePixel; const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw const bool thick_line = (thickness > _FringeScale); + + // If this assert triggers on legacy code: + // - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced. + // Read more details near AddRect() + see "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8. IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); if (Flags & ImDrawListFlags_AntiAliasedLines) @@ -1503,13 +1508,14 @@ void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float th void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, float thickness, ImDrawFlags flags) { // If this assert triggers on legacy code: - // - 1.92.8 (2025/04): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.92.8 (2025/05): swapped two last parameters order: flags, thickness --> thickness, flags. This should normally be caught by compile-time type-checking. + // - 1.92.8 (2025/05): changed value of ImDrawList_Closed which was previously guaranteed to be == 1. Hardcoded use of 1 or true should be replaced. // - 1.82.0 (2021/03): changed ImDrawCornerFlags to ImDrawFlags_RoundCornersXXX values. // If you used hard-coded 1 to 15 or ~0 in flags to configure corner rounding use the new flags! // - Hard coded support for ~0 == ImDrawFlags_RoundCornersAll. // - Hard coded support for values 0x01 to 0x0F (matching 15 out of 16 old flags combinations) --> see FixRectCornerFlags() in <1.90 code. // - Hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'. - // See "API BREAKING CHANGES" section for 1.82 and 1.90. + // See "API BREAKING CHANGES" section for 1.82, 1.90 and 1.92.8. IM_ASSERT_USER_ERROR_RET((flags & ImDrawFlags_InvalidMask_) == 0, "Incorrect parameter. Did you swap 'thickness' and 'flags'?"); // Or misuse of legacy hard-coded ImDrawCornerFlags values if ((col & IM_COL32_A_MASK) == 0) From 33bb693b4cb965e2f7a0feeee0a442814ef84cc0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 28 May 2026 15:37:29 +0200 Subject: [PATCH 46/60] DrawList: skip PathLineTo/PathStroke calls for common AddLine(), AddLineH(), AddLineV() functions. (#4091) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 15 ++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 17aadb6e..46ee7cdc 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,8 @@ Other Changes: - Fonts: - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to disable embedding either fonts separately. (#9407) +- DrawList: + - Minor optimization to AddLine(), AddLineH(), AddLineV() functions. (#4091) - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 690b7e5b..093f0f14 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1480,27 +1480,24 @@ void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float th { if ((col & IM_COL32_A_MASK) == 0) return; - PathLineTo(p1 + ImVec2(0.5f, 0.5f)); - PathLineTo(p2 + ImVec2(0.5f, 0.5f)); - PathStroke(col, thickness); + const ImVec2 points[2] = { ImVec2(p1.x + 0.5f, p1.y + 0.5f), ImVec2(p2.x + 0.5f, p2.y + 0.5f) }; + AddPolyline(points, 2, col, thickness); } void ImDrawList::AddLineH(float min_x, float max_x, float y, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; - PathLineTo(ImVec2(min_x + 0.5f, y + 0.5f)); // Same as AddLine() above. - PathLineTo(ImVec2(max_x + 0.5f, y + 0.5f)); - PathStroke(col, thickness); + const ImVec2 points[2] = { ImVec2(min_x + 0.5f, y + 0.5f), ImVec2(max_x + 0.5f, y + 0.5f) }; // Same as AddLine() above. + AddPolyline(points, 2, col, thickness); } void ImDrawList::AddLineV(float x, float min_y, float max_y, ImU32 col, float thickness) { if ((col & IM_COL32_A_MASK) == 0) return; - PathLineTo(ImVec2(x + 0.5f, min_y + 0.5f)); // Same as AddLine() above. - PathLineTo(ImVec2(x + 0.5f, max_y + 0.5f)); - PathStroke(col, thickness); + const ImVec2 points[2] = { ImVec2(x + 0.5f, min_y + 0.5f), ImVec2(x + 0.5f, max_y + 0.5f) }; // Same as AddLine() above. + AddPolyline(points, 2, col, thickness); } // p_min = upper-left, p_max = lower-right From 12b797755501ae8eced5f6fb33105d8ddc1d1cbc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 28 May 2026 19:19:11 +0200 Subject: [PATCH 47/60] Tweak CalcTextSize() awkward width rounding/ceiling code to reduce floating-point imprecisions altering the result by 1 even at relatively small width. (#791) + apply same fudge factor to less important roundings. ceilf() is still measurable e.g. ballpark +0.5 for 200k calls. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 9 ++++----- imgui_draw.cpp | 2 +- imgui_widgets.cpp | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 46ee7cdc..1c553583 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -55,6 +55,8 @@ Other Changes: - Fonts: - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to disable embedding either fonts separately. (#9407) + - Tweak CalcTextSize() awkward width rounding/ceiling code to reduce floating-point + imprecisions altering the result by 1 even at relatively small width. (#791) - DrawList: - Minor optimization to AddLine(), AddLineH(), AddLineV() functions. (#4091) - Demo: diff --git a/imgui.cpp b/imgui.cpp index ecd3bc1c..735deb9d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6199,11 +6199,10 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); // Round - // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out. - // FIXME: Investigate using ceilf or e.g. - // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c - // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html - text_size.x = IM_TRUNC(text_size.x + 0.99999f); + // FIXME: This has been here since Dec 2015 (7b0bf230 then 4622fa4b6) but down the line we want this out. See #791. + // Investigate using ceilf or e.g. https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c, https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html + // The problem is that ceilf() has a measurable cost. Not high, but measurable! + text_size.x = IM_TRUNC(text_size.x + 0.999f); return text_size; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 093f0f14..b112f471 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -656,7 +656,7 @@ void ImDrawList::_OnChangedVtxOffset() int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count - const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy + const int radius_idx = (int)(radius + 0.999f); // ceil to never reduce accuracy if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value else diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3807f1f0..a98cb693 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1748,14 +1748,14 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); - const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; const float sep1_x1 = pos.x; const float sep2_x2 = bb.Max.x; - const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); + const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.999f); const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f); const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN @@ -8952,7 +8952,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get // Tooltip on hover if (hovered && inner_bb.Contains(g.IO.MousePos)) { - const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.999f); const int v_idx = (int)(t * item_count); IM_ASSERT(v_idx >= 0 && v_idx < values_count); From 75f985998b0b0759470a46cd02e73d1c675487fa Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 29 May 2026 15:59:44 +0200 Subject: [PATCH 48/60] Using custom ceilf inline impl in ImGui::CalcTextSize(). Amend 7b0bf230, 4622fa4b6, 12b7977. (#791) --- imgui.cpp | 7 +++---- imgui.h | 2 +- imgui_internal.h | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 735deb9d..28fc2aa3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6199,10 +6199,9 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); // Round - // FIXME: This has been here since Dec 2015 (7b0bf230 then 4622fa4b6) but down the line we want this out. See #791. - // Investigate using ceilf or e.g. https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c, https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html - // The problem is that ceilf() has a measurable cost. Not high, but measurable! - text_size.x = IM_TRUNC(text_size.x + 0.999f); + // (see 7b0bf230, 4622fa4b6, #791 for details about this.) + // FIXME: Add a way to disable this. + text_size.x = ImCeilFast(text_size.x); return text_size; } diff --git a/imgui.h b/imgui.h index cc80fc80..2aa8cb0d 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.9 WIP" -#define IMGUI_VERSION_NUM 19281 +#define IMGUI_VERSION_NUM 19282 #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_internal.h b/imgui_internal.h index 70f2bad3..4b9e18dd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -531,6 +531,7 @@ inline float ImFloor(float f) { return inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } inline float ImTrunc64(float f) { return (float)(ImS64)(f); } inline float ImRound64(float f) { return (float)(ImS64)(f + 0.5f); } // FIXME: Positive values only. +inline float ImCeilFast(float f) { int i = (int)f; return (float)(i + (f > (float)i)); } // Consider using the the bit-hack version (search for "0x1p120f"). inline int ImModPositive(int a, int b) { return (a + b) % b; } inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } From 1d33ea939f702e0fb1b4201f1032ee63993ba3e4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 29 May 2026 18:10:29 +0200 Subject: [PATCH 49/60] DrawList: added ImDrawListFlags_NoTextPixelSnap to disable snapping of AddText() coordinates for a given scope. (#3437, #9417, #2291) This may evolve into a per-call ImDrawFlags option as well. + clip_rect.Max.y early out may be applied before truncation. --- docs/CHANGELOG.txt | 10 ++++++---- imgui.h | 1 + imgui_draw.cpp | 20 +++++++++++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1c553583..932ebe12 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,15 +50,17 @@ Other Changes: - Clicking on a window's empty-space to move/focus a window checks for lack of queued focus request. (#9382) - InputText: - - Added style.InputTextCursorSize to configure cursor/caret thickness. (#7031, #9409) - This is automatically scaled by style.ScaleAllSizes(). + - Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409) + This is automatically scaled by `style.ScaleAllSizes()`. - Fonts: - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to disable embedding either fonts separately. (#9407) - - Tweak CalcTextSize() awkward width rounding/ceiling code to reduce floating-point + - Tweak `CalcTextSize()` awkward width rounding/ceiling code to reduce floating-point imprecisions altering the result by 1 even at relatively small width. (#791) - DrawList: - - Minor optimization to AddLine(), AddLineH(), AddLineV() functions. (#4091) + - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) + - Added `ImDrawListFlags_NoTextPixelSnap` to disable snapping of AddText() + coordinates for a given scope. (#3437, #9417, #2291) - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). diff --git a/imgui.h b/imgui.h index 2aa8cb0d..05023704 100644 --- a/imgui.h +++ b/imgui.h @@ -3279,6 +3279,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_NoTextPixelSnap = 1 << 4, // [Internal] Disable automatically snapping AddText() calls to pixel boundaries. }; // Draw command list diff --git a/imgui_draw.cpp b/imgui_draw.cpp index b112f471..a78a35ac 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -5764,8 +5764,13 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im if (glyph->Colored) col |= ~IM_COL32_A_MASK; float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f; - float x = IM_TRUNC(pos.x); - float y = IM_TRUNC(pos.y); + float x = pos.x; + float y = pos.y; + if ((draw_list->Flags & ImDrawListFlags_NoTextPixelSnap) == 0) + { + x = IM_TRUNC(x); + y = IM_TRUNC(y); + } float x1 = x + glyph->X0 * scale; float x2 = x + glyph->X1 * scale; @@ -5797,12 +5802,17 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im // DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2026. Use ImDrawList::AddText(). void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags) { - // Align to be pixel perfect begin: - float x = IM_TRUNC(pos.x); - float y = IM_TRUNC(pos.y); + // Align to be pixel perfect + float x = pos.x; + float y = pos.y; if (y > clip_rect.w) return; + if ((draw_list->Flags & ImDrawListFlags_NoTextPixelSnap) == 0) + { + x = IM_TRUNC(x); + y = IM_TRUNC(y); + } if (!text_end) text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. From cac16b0d1691e6985dbfbd7e773b00259af45dd7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 29 May 2026 18:39:17 +0200 Subject: [PATCH 50/60] DrawList: rename ImDrawListFlags_NoTextPixelSnap -> ImDrawListFlags_TextNoPixelSnap. (#3437, #9417, #2291) Amend 1d33ea9 --- docs/CHANGELOG.txt | 2 +- imgui.h | 2 +- imgui_draw.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 932ebe12..55803f33 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -59,7 +59,7 @@ Other Changes: imprecisions altering the result by 1 even at relatively small width. (#791) - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) - - Added `ImDrawListFlags_NoTextPixelSnap` to disable snapping of AddText() + - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() coordinates for a given scope. (#3437, #9417, #2291) - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' diff --git a/imgui.h b/imgui.h index 05023704..84c99172 100644 --- a/imgui.h +++ b/imgui.h @@ -3279,7 +3279,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. - ImDrawListFlags_NoTextPixelSnap = 1 << 4, // [Internal] Disable automatically snapping AddText() calls to pixel boundaries. + ImDrawListFlags_TextNoPixelSnap = 1 << 4, // [Internal] Disable automatically snapping AddText() calls to pixel boundaries. }; // Draw command list diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a78a35ac..090efb4d 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -5766,7 +5766,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f; float x = pos.x; float y = pos.y; - if ((draw_list->Flags & ImDrawListFlags_NoTextPixelSnap) == 0) + if ((draw_list->Flags & ImDrawListFlags_TextNoPixelSnap) == 0) { x = IM_TRUNC(x); y = IM_TRUNC(y); @@ -5808,7 +5808,7 @@ begin: float y = pos.y; if (y > clip_rect.w) return; - if ((draw_list->Flags & ImDrawListFlags_NoTextPixelSnap) == 0) + if ((draw_list->Flags & ImDrawListFlags_TextNoPixelSnap) == 0) { x = IM_TRUNC(x); y = IM_TRUNC(y); From 70f02b05581b1b38a981ac375f25c81276b5c97a Mon Sep 17 00:00:00 2001 From: Dex <60656530+dexmoh@users.noreply.github.com> Date: Sun, 31 May 2026 13:54:30 +0200 Subject: [PATCH 51/60] Docs: Fix small grammar mistake in README. (#9423) --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 283dd479..cc73e28e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -43,7 +43,7 @@ Dear ImGui is particularly suited to integration in game engines (for tooling), **Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. -C++20 users wishing to use a module may the use [stripe2933/imgui-module](https://github.com/stripe2933/imgui-module) third-party extension. +C++20 users wishing to use a module may use the [stripe2933/imgui-module](https://github.com/stripe2933/imgui-module) third-party extension. See the [Getting Started & Integration](#getting-started--integration) section of this document for more details. From 3b99fc48832ab8d3930cca0d38a6327239df30a7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jun 2026 14:23:17 +0200 Subject: [PATCH 52/60] ColorButton: small rendering tweak/optimization for the alpha checkerboard. Inverting the pattern means that for our typical 3x3 grid we don't need to draw 1+4 rounded rectangles, only 1. This also fix the slightly rounding mismatch when switching from a=255 to a<255 paths. --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 55803f33..76d85516 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -61,6 +61,8 @@ Other Changes: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() coordinates for a given scope. (#3437, #9417, #2291) +- ColorButton: + - Small rendering tweak/optimization for the alpha checkerboard. - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 090efb4d..d8f9bf8e 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -6198,8 +6198,8 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p flags = ImDrawFlags_RoundCornersDefault_; if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) { - ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); - ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col)); draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags); int yi = 0; @@ -6208,12 +6208,12 @@ void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); if (y2 <= y1) continue; - for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) + for (float x = p_min.x + grid_off.x + ((yi ^ 1) & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) { float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); if (x2 <= x1) continue; - ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; + ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone; // FIXME: Could use CalcRoundingFlagsForRectInRect() if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; } if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; } From 3495e329f38b1e11df6607b26bdbadcdcee5b423 Mon Sep 17 00:00:00 2001 From: Flexan Date: Mon, 1 Jun 2026 15:34:00 +0200 Subject: [PATCH 53/60] Fixed small typo in demo code (#9425) --- imgui_demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 673a0248..e0bbd9af 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4121,7 +4121,7 @@ static void DemoWindowWidgetsTooltips() ImGui::BeginDisabled(); ImGui::Button("Disabled item", sz); if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) - ImGui::SetTooltip("I am a a tooltip for a disabled item."); + ImGui::SetTooltip("I am a tooltip for a disabled item."); ImGui::EndDisabled(); ImGui::TreePop(); From f241419b7856eda54908534dfea578d83bbbf0c2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jun 2026 16:48:48 +0200 Subject: [PATCH 54/60] Tabs: use AddRectFilled(). --- imgui_widgets.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index a98cb693..6a660527 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -10873,13 +10873,9 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI IM_UNUSED(flags); IM_ASSERT(width > 0.0f); const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); - const float y1 = bb.Min.y + 1.0f; + const float y1 = bb.Min.y + 1.0f; // Leave a bit of room in title bars. const float y2 = bb.Max.y - g.Style.TabBarBorderSize; - draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); - draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); - draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); - draw_list->PathLineTo(ImVec2(bb.Max.x, y2)); - draw_list->PathFillConvex(col); + draw_list->AddRectFilled(bb.Min, ImVec2(bb.Max.x, y2), col, rounding, ImDrawFlags_RoundCornersTop); if (g.Style.TabBorderSize > 0.0f) { draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2)); From 5aa0393a15f42f3d9274f6f9ea540b0b0e448ac5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 1 Jun 2026 17:23:11 +0200 Subject: [PATCH 55/60] DrawList: don't mark ImDrawListFlags_TextNoPixelSnap as internal in comments. (#3437, #9417, #2291) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index 84c99172..d0c5ee8c 100644 --- a/imgui.h +++ b/imgui.h @@ -3279,7 +3279,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. - ImDrawListFlags_TextNoPixelSnap = 1 << 4, // [Internal] Disable automatically snapping AddText() calls to pixel boundaries. + ImDrawListFlags_TextNoPixelSnap = 1 << 4, // Disable automatically snapping AddText() calls to pixel boundaries. }; // Draw command list From 045a0907f4824ed3bfdb95bc8721f197f7d9734e Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 2 Jun 2026 18:05:23 +0200 Subject: [PATCH 56/60] Docs: Fonts.md: added notes about TexMinWidth,TexMinHeight. Probably misplaced: the document could have a section about memory usage considerations? --- docs/FONTS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/FONTS.md b/docs/FONTS.md index 1e51cde4..f0fbd85f 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -77,6 +77,17 @@ Some solutions: You can use the `ImFontGlyphRangesBuilder` for this purpose and rebuilding your atlas between frames when new characters are needed. This will be the biggest win! - Set `io.Fonts.Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;` to disable rounding the texture height to the next power of two. +### (5) Reduce texture resizes/copy on startup + +🆕 Since 1.92, the ImFontAtlas is initially created using a 512x128 texture, then grows as glyphs and fonts are registered. Growth leads to a copy, and both the old and new sized textures are present in memory. If you use known fonts and want to reduce initial growth, you may set `TexMinWidth` and `TexMinHeight` during initializaton. + +```cpp +ImFontAtlas* atlas = io.Fonts; +atlas->TexMinWidth = 1024; +atlas->TexMinHeight = 1024; +atlas->AddFont(...); +``` + ##### [Return to Index](#index) --------------------------------------- From a054a016e28d8fb13a5f2d48b7c51647aa28bc36 Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 2 Jun 2026 18:09:48 +0200 Subject: [PATCH 57/60] Docs: Fonts: amends. --- docs/FONTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FONTS.md b/docs/FONTS.md index f0fbd85f..ac93bca0 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -79,7 +79,7 @@ Some solutions: ### (5) Reduce texture resizes/copy on startup -🆕 Since 1.92, the ImFontAtlas is initially created using a 512x128 texture, then grows as glyphs and fonts are registered. Growth leads to a copy, and both the old and new sized textures are present in memory. If you use known fonts and want to reduce initial growth, you may set `TexMinWidth` and `TexMinHeight` during initializaton. +🆕 Since 1.92, the ImFontAtlas is initially created using a 512x128 texture, then grows as glyphs and fonts are used. Atlas growth leads to alloc+copy, and both the old and new sized textures are present in memory for a short time (typically one frame). If you use known fonts and want to reduce initial growth, you may set `TexMinWidth` and `TexMinHeight` during initializaton. ```cpp ImFontAtlas* atlas = io.Fonts; From 995e4a65ff44892cc27cf28e5ee4e988337812e9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jun 2026 18:37:01 +0200 Subject: [PATCH 58/60] (Breaking) TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 1 + imgui.h | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 76d85516..9c3c8e68 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,9 @@ HOW TO UPDATE? Breaking Changes: +- TreeNode: commented out legacy name `ImGuiTreeNodeFlags_SpanTextWidth` which + was obsoleted in 1.90.7 (May 2024). Use `ImGuiTreeNodeFlags_SpanLabelWidth`. + Other Changes: - Windows: diff --git a/imgui.cpp b/imgui.cpp index 28fc2aa3..c56e9eff 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,6 +395,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/06/02 (1.92.9) - TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. - 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). - Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); - After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); diff --git a/imgui.h b/imgui.h index d0c5ee8c..9f7c3a16 100644 --- a/imgui.h +++ b/imgui.h @@ -1331,7 +1331,7 @@ enum ImGuiTreeNodeFlags_ #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent, // Renamed in 1.92.0 - ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7 + //ImGuiTreeNodeFlags_SpanTextWidth = ImGuiTreeNodeFlags_SpanLabelWidth, // Renamed in 1.90.7 //ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 #endif }; @@ -2614,7 +2614,7 @@ struct ImGuiIO //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92 (June 2025) + float FontGlobalScale; // Moved io.FontGlobalScale to style.FontScaleMain in 1.92.0 (June 2025) // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO. // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete). @@ -3925,7 +3925,7 @@ struct ImFont IMGUI_API void RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL); IMGUI_API void RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); } + inline const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); } // Obsoleted old name in 1.92.0. Note how `scale` was to `size`. #endif // [Internal] Don't use! From 5a42cddcd2408074ece1f9231ceeb0ca221e6efe Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jun 2026 18:40:14 +0200 Subject: [PATCH 59/60] Fixed a build issue when defined IMGUI_API + IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#9424) Amend 6df50a0 --- imgui.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 9f7c3a16..c4828e59 100644 --- a/imgui.h +++ b/imgui.h @@ -3431,8 +3431,8 @@ struct ImDrawList inline void PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); } // RENAMED in 1.92.0 inline void PopTextureID() { PopTexture(); } // RENAMED in 1.92.0 #else - IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; - IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete; + void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding /*= 0.0f*/, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; + void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness) = delete; inline void PathStroke(ImU32 col, ImDrawFlags flags /*= 0*/, float thickness /*= 1.0f*/) = delete; #endif //inline void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024) From c4eaac6d4856d4136b31f7db6e3974c6d846db4d Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 2 Jun 2026 19:04:14 +0200 Subject: [PATCH 60/60] Fonts: fixed an issue where passing a manually created ImFontAtlas to CreateContext() would incorrectly destroy it in DestroyContext() when ref-count gets back to zero. (#9426) # Conflicts: # docs/CHANGELOG.txt --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9c3c8e68..4f8cf705 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -60,6 +60,8 @@ Other Changes: disable embedding either fonts separately. (#9407) - Tweak `CalcTextSize()` awkward width rounding/ceiling code to reduce floating-point imprecisions altering the result by 1 even at relatively small width. (#791) + - Fixed an issue where passing a manually created ImFontAtlas to CreateContext() would + incorrectly destroy it in DestroyContext() when ref-count gets back to zero. (#9426) - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) - Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText() diff --git a/imgui.cpp b/imgui.cpp index c56e9eff..516bd9de 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4476,7 +4476,7 @@ void ImGui::Shutdown() for (ImFontAtlas* atlas : g.FontAtlases) { UnregisterFontAtlas(atlas); - if (atlas->RefCount == 0) + if (atlas->RefCount == 0 && atlas->OwnerContext == &g) { atlas->Locked = false; IM_DELETE(atlas);