From 068e0555108d5fc93685f20ea6ee3ba599530d3d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 15 May 2026 16:34:53 +0200 Subject: [PATCH 01/11] 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 d3041d5f9..dce53339b 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 b2d8c974a..70f2bad39 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 02/11] 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 c7557c8b9..aa5fa6a18 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 03/11] 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 abea19dc9..d5d77bee5 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 aa5fa6a18..c0293949c 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 816642ed2..b6763d426 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 b95dd8a2b..673a0248a 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 04/11] 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 d789f25fd..787329bf6 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 d5d77bee5..59ad650bf 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 05/11] 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 b6763d426..787417667 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 55b9e4cd8..63e3f5e03 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 06/11] 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 59ad650bf..ce0f751a6 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 c0293949c..25e28ed10 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 787417667..1c697eb39 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 63e3f5e03..3807f1f00 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 07/11] 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 dce53339b..c695751af 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 08/11] 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 ce0f751a6..091620ec2 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 09/11] 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 091620ec2..0cfd5458f 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 0d843be26..0a4ca08af 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 c695751af..6cee3f467 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 10/11] 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 0cfd5458f..17aadb6ea 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 25e28ed10..ecd3bc1c6 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 1c697eb39..cc80fc80f 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 6cee3f467..690b7e5b0 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 11/11] 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 17aadb6ea..46ee7cdc4 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 690b7e5b0..093f0f140 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