From 2128e2f44b7ecd0250cfd47921e03d87e4506f4c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Oct 2025 12:16:48 +0200 Subject: [PATCH 01/13] Style: added ImGuiCol_UnsavedMarker. (#8983) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 3 ++- imgui.h | 1 + imgui_draw.cpp | 3 +++ imgui_widgets.cpp | 4 ++-- 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 66846d00e..bc6fd0063 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,8 @@ Other Changes: The grip is not visible before hovering to reduce clutter. - InputText: fixed single-line InputText() not applying fine character clipping properly (regression in 1.92.3). (#8967) [@Cyphall] +- Style: added ImGuiCol_UnsavedMarker, color of the unsaved document marker when + using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, diff --git a/imgui.cpp b/imgui.cpp index 8928335aa..6b501f343 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3693,6 +3693,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_TreeLines: return "TreeLines"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; + case ImGuiCol_UnsavedMarker: return "UnsavedMarker"; case ImGuiCol_NavCursor: return "NavCursor"; case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; @@ -7169,7 +7170,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; if (marker_pos.x > layout_r.Min.x) { - RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker)); clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); } } diff --git a/imgui.h b/imgui.h index b006023a2..9589fa561 100644 --- a/imgui.h +++ b/imgui.h @@ -1774,6 +1774,7 @@ enum ImGuiCol_ ImGuiCol_TextSelectedBg, // Selected text inside an InputText ImGuiCol_TreeLines, // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target + ImGuiCol_UnsavedMarker, // Unsaved Document marker (in window title and tabs) ImGuiCol_NavCursor, // Color of keyboard/gamepad navigation cursor/rectangle, when visible ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 082af59ac..a2c616012 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -240,6 +240,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); @@ -305,6 +306,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); @@ -371,6 +373,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 592a76904..45a4dcf62 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -10716,8 +10716,8 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered); if (unsaved_marker_visible) { - const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz)); - RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); + ImVec2 bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5f; + RenderBullet(draw_list, bullet_pos, GetColorU32(ImGuiCol_UnsavedMarker)); } else if (close_button_visible) { From 09e15e8c9dd1c5a780d51c8f7c8ddfdb9d400e84 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Oct 2025 13:55:54 +0200 Subject: [PATCH 02/13] Nav: fixed typo. --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index fe79a862f..c2d858580 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2306,7 +2306,7 @@ struct ImGuiContext float NavHighlightActivatedTimer; ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data. ImS8 NavCursorHideFrames; //ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606. From 4b858cf5d1327fa5e3a84ae8044ff2026f3a43d3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Oct 2025 16:55:11 +0200 Subject: [PATCH 03/13] Made tooltip windows inherit parent. (#8982, #1345) Intent here was for a manually focused tooltip to not steal title bar highlight. --- imgui.cpp | 4 ++-- imgui.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 6b501f343..2ba3aacd9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7187,7 +7187,7 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags window->RootWindow = parent_window->RootWindow; if (parent_window && (flags & ImGuiWindowFlags_Popup)) window->RootWindowPopupTree = parent_window->RootWindowPopupTree; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip))) window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened) { @@ -7296,7 +7296,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; + ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); // We allow window memory to be compacted so recreate the base stack when needed. diff --git a/imgui.h b/imgui.h index 9589fa561..24c6423b4 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.4 WIP" -#define IMGUI_VERSION_NUM 19234 +#define IMGUI_VERSION_NUM 19235 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 From ee3d16f15070b4de6656cc68e39e89fa9a469436 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 6 Oct 2025 18:39:10 +0200 Subject: [PATCH 04/13] Demo: fixed layout issue in "Layout & Scrolling -> Scrolling" section. --- docs/CHANGELOG.txt | 13 +++++++------ imgui_demo.cpp | 15 +++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index bc6fd0063..496bf1d9f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -68,9 +68,6 @@ Other Changes: using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) -- Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, - Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc. - See misc/debuggers/ for details. (#8950) [@mentlerd] - Textures: fixed a crash if texture status is set to _WantDestroy by a backend after it had already been destroyed. This would typically happen when calling backend's ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811) @@ -83,12 +80,16 @@ Other Changes: - Textures: fixed not updating ImTextureData's RefCount when destroying a context using a shared ImFontAtlas, leading standard backends to not properly free texture resources. (#8975) [@icrashstuff] -- CI: Updates Windows CI scripts to generate/use VulkanSDK. (#8925, #8778) [@yaz0r] +- Demo: fixed layout issue in "Layout & Scrolling -> Scrolling" section. +- Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, + Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc. + See misc/debuggers/ for details. (#8950) [@mentlerd] +- CI: updated Windows CI scripts to generate/use VulkanSDK. (#8925, #8778) [@yaz0r] +- Docs: updated FAQ with new "What is the difference between Dear ImGui and + traditional UI toolkits?" entry. (#8862) - Backends: all backends call ImGuiPlatformIO::ClearPlatformHandlers() and ClearRendererHandlers() on shutdown, so as not to leave function pointers which may be dangling when using backend in e.g. DLL. (#8945, #2769) -- Docs: updated FAQ with new "What is the difference between Dear ImGui and - traditional UI toolkits?" entry. (#8862) - Backends: DirectX12: reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963, #8465) [@RT2Code] - Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code] diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e3991a60e..3a6bcf5a5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4795,15 +4795,18 @@ static void DemoWindowLayout() ImGui::Checkbox("Decoration", &enable_extra_decorations); + ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); + ImGui::SameLine(); ImGui::Checkbox("Track", &enable_track); - ImGui::PushItemWidth(100); - ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); - bool scroll_to_off = ImGui::Button("Scroll Offset"); - ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); + bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); + ImGui::SameLine(); + scroll_to_off |= ImGui::Button("Scroll Offset"); - bool scroll_to_pos = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); + bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");; + ImGui::SameLine(); + scroll_to_pos |= ImGui::Button("Scroll To Pos"); ImGui::PopItemWidth(); if (scroll_to_off || scroll_to_pos) From fd0873c61ffc3e41415e00441ced4d2e1905553c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 7 Oct 2025 15:59:45 +0200 Subject: [PATCH 05/13] Test Engine: fixed mismatched macro signature when disabled. The macro was actually unused in our code if IMGUI_ENABLE_TEST_ENGINE is not defined, but can affect third-party code. --- imgui_internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_internal.h b/imgui_internal.h index c2d858580..e65cbcab7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3921,7 +3921,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) +#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif From bee2720faac5f33401fcda7fa9eb51113d13e8d6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Oct 2025 18:44:07 +0200 Subject: [PATCH 06/13] Docs: clarify meaning/purpose of IMGUI_ENABLE_FREETYPE. (#8993) --- imconfig.h | 1 + misc/freetype/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/imconfig.h b/imconfig.h index 4dab1b604..0e7cf6182 100644 --- a/imconfig.h +++ b/imconfig.h @@ -83,6 +83,7 @@ //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience. // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE diff --git a/misc/freetype/README.md b/misc/freetype/README.md index e1bd0198b..6ea6ad1ae 100644 --- a/misc/freetype/README.md +++ b/misc/freetype/README.md @@ -7,7 +7,7 @@ Build font atlases using FreeType instead of stb_truetype (which is the default 1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype --triplet=x64-windows`, `vcpkg integrate install`). 2. Add imgui_freetype.h/cpp alongside your project files. -3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file +3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file to make Dear ImGui automatically use the imgui_freetype loader. If your copy Dear ImGui is precompiled, you can always enable imgui_freetype by calling ImFontAtlas::SetFontLoader(). ### About Gamma Correct Blending From 2b770a029ba01b78c6e27d6fc74de308eaefb634 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Oct 2025 19:00:34 +0200 Subject: [PATCH 07/13] InputText: fixed an infinite loop error happening if a custom input text callback modifies/clear BufTextLen before calling InsertChars(). (#8994, #3237) + misc comments. --- docs/CHANGELOG.txt | 5 +++++ imgui_demo.cpp | 2 +- imgui_internal.h | 2 +- imgui_widgets.cpp | 8 +++++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 496bf1d9f..9c5b50988 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,11 @@ Other Changes: The grip is not visible before hovering to reduce clutter. - InputText: fixed single-line InputText() not applying fine character clipping properly (regression in 1.92.3). (#8967) [@Cyphall] +- InputText: fixed an infinite loop error happening if a custom input text + callback modifies/clear BufTextLen before calling InsertChars(). + (regression from 1.92.3). Note that this never really worked correctly, but + previously it would only temporary wreck cursor position, and since 1.92.3 it + would go in an infinite loop. (#8994, #3237) - Style: added ImGuiCol_UnsavedMarker, color of the unsaved document marker when using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 3a6bcf5a5..dec1bad40 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4804,7 +4804,7 @@ static void DemoWindowLayout() ImGui::SameLine(); scroll_to_off |= ImGui::Button("Scroll Offset"); - bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");; + bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); ImGui::SameLine(); scroll_to_pos |= ImGui::Button("Scroll To Pos"); ImGui::PopItemWidth(); diff --git a/imgui_internal.h b/imgui_internal.h index e65cbcab7..1510271a0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2484,7 +2484,7 @@ struct ImGuiContext ImGuiWindow* LogWindow; ImFileHandle LogFile; // If != NULL log to stdout/ file ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - const char* LogNextPrefix; + const char* LogNextPrefix; // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully! const char* LogNextSuffix; float LogLinePosY; bool LogLineFirstItem; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 45a4dcf62..a3a4ae1cf 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3941,6 +3941,7 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c { ImGuiContext& g = *ctx; ImGuiInputTextState* obj = &g.InputTextState; + IM_ASSERT(text_end_display >= text_begin && text_end_display <= text_end); return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags); } @@ -4321,11 +4322,12 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); Buf[BufTextLen + new_text_len] = '\0'; - if (CursorPos >= pos) - CursorPos += new_text_len; - SelectionStart = SelectionEnd = CursorPos; BufDirty = true; BufTextLen += new_text_len; + if (CursorPos >= pos) + CursorPos += new_text_len; + CursorPos = ImClamp(CursorPos, 0, BufTextLen); + SelectionStart = SelectionEnd = CursorPos; } void ImGui::PushPasswordFont() From 5af650fc6d27daf585edbd9ac872465f756a65e9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 9 Oct 2025 18:54:16 +0200 Subject: [PATCH 08/13] Examples: GLFW+WGPU: various tweaks to reduce diff with #8381 --- backends/imgui_impl_wgpu.cpp | 2 +- backends/imgui_impl_wgpu.h | 2 +- examples/example_glfw_wgpu/main.cpp | 63 ++++++++++++++++------------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 9f731c8f1..3045f9c7c 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -46,7 +46,7 @@ #include "imgui.h" -// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN +// When targeting native platforms (i.e. NOT Emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN // or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details. #ifndef __EMSCRIPTEN__ #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 61d2d23c0..499c899a0 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -32,7 +32,7 @@ // Initialization data, for ImGui_ImplWGPU_Init() struct ImGui_ImplWGPU_InitInfo { - WGPUDevice Device; + WGPUDevice Device = nullptr; int NumFramesInFlight = 3; WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined; WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined; diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index 670dd08c2..59bb505a9 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for using GLFW + WebGPU +// Dear ImGui: standalone example application for GLFW + WebGPU // - Emscripten is supported for publishing on web. See https://emscripten.org. // - Dawn is used as a WebGPU implementation on desktop. @@ -12,36 +12,32 @@ #include "imgui_impl_glfw.h" #include "imgui_impl_wgpu.h" #include +#include +// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. #ifdef __EMSCRIPTEN__ #include #include #include +#include "../libs/emscripten/emscripten_mainloop_stub.h" #else #include #endif - -#include #include #include -// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. -#ifdef __EMSCRIPTEN__ -#include "../libs/emscripten/emscripten_mainloop_stub.h" -#endif - -// Global WebGPU required states +// Data static WGPUInstance wgpu_instance = nullptr; static WGPUDevice wgpu_device = nullptr; static WGPUSurface wgpu_surface = nullptr; static WGPUTextureFormat wgpu_preferred_fmt = WGPUTextureFormat_RGBA8Unorm; static WGPUSwapChain wgpu_swap_chain = nullptr; -static int wgpu_swap_chain_width = 1280; -static int wgpu_swap_chain_height = 800; +static int wgpu_surface_width = 1280; +static int wgpu_surface_height = 800; // Forward declarations static bool InitWGPU(GLFWwindow* window); -static void CreateSwapChain(int width, int height); +static void ResizeSurface(int width, int height); static void glfw_error_callback(int error, const char* description) { @@ -72,19 +68,23 @@ int main(int, char**) // Make sure GLFW does not initialize any graphics context. // This needs to be done explicitly later. glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - GLFWwindow* window = glfwCreateWindow(wgpu_swap_chain_width, wgpu_swap_chain_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); + + // Create window + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only + wgpu_surface_width *= main_scale; + wgpu_surface_height *= main_scale; + GLFWwindow* window = glfwCreateWindow(wgpu_surface_width, wgpu_surface_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); if (window == nullptr) return 1; // Initialize the WebGPU environment if (!InitWGPU(window)) { - if (window) - glfwDestroyWindow(window); + glfwDestroyWindow(window); glfwTerminate(); return 1; } - CreateSwapChain(wgpu_swap_chain_width, wgpu_swap_chain_height); + ResizeSurface(wgpu_surface_width, wgpu_surface_height); glfwShowWindow(window); // Setup Dear ImGui context @@ -98,6 +98,11 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose) + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOther(window, true); #ifdef __EMSCRIPTEN__ @@ -117,15 +122,14 @@ int main(int, char**) // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. // - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author! // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - // - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details. - //io.Fonts->AddFontDefault(); + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefault(); #ifndef IMGUI_DISABLE_FILE_FUNCTIONS //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf"); - //io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf"); //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf"); //IM_ASSERT(font != nullptr); #endif @@ -160,10 +164,10 @@ int main(int, char**) // React to changes in screen size int width, height; glfwGetFramebufferSize((GLFWwindow*)window, &width, &height); - if (width != wgpu_swap_chain_width || height != wgpu_swap_chain_height) + if (width != wgpu_surface_width || height != wgpu_surface_height) { ImGui_ImplWGPU_InvalidateDeviceObjects(); - CreateSwapChain(width, height); + ResizeSurface(width, height); ImGui_ImplWGPU_CreateDeviceObjects(); } @@ -238,8 +242,8 @@ int main(int, char**) WGPUCommandBufferDescriptor cmd_buffer_desc = {}; WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); - WGPUQueue queue = wgpuDeviceGetQueue(wgpu_device); - wgpuQueueSubmit(queue, 1, &cmd_buffer); + WGPUQueue wgpu_queue = wgpuDeviceGetQueue(wgpu_device); + wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer); #ifndef __EMSCRIPTEN__ wgpuSwapChainPresent(wgpu_swap_chain); @@ -311,10 +315,10 @@ static bool InitWGPU(GLFWwindow* window) #endif #ifdef __EMSCRIPTEN__ - wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {}; - html_surface_desc.selector = "#canvas"; + wgpu::SurfaceDescriptorFromCanvasHTMLSelector canvas_desc = {}; + canvas_desc.selector = "#canvas"; wgpu::SurfaceDescriptor surface_desc = {}; - surface_desc.nextInChain = &html_surface_desc; + surface_desc.nextInChain = &canvas_desc; wgpu::Surface surface = instance.CreateSurface(&surface_desc); wgpu::Adapter adapter = {}; @@ -326,6 +330,7 @@ static bool InitWGPU(GLFWwindow* window) wgpu_preferred_fmt = WGPUTextureFormat_BGRA8Unorm; #endif + // Moving Dawn objects into WGPU handles wgpu_instance = instance.MoveToCHandle(); wgpu_surface = surface.MoveToCHandle(); @@ -334,12 +339,12 @@ static bool InitWGPU(GLFWwindow* window) return true; } -static void CreateSwapChain(int width, int height) +static void ResizeSurface(int width, int height) { if (wgpu_swap_chain) wgpuSwapChainRelease(wgpu_swap_chain); - wgpu_swap_chain_width = width; - wgpu_swap_chain_height = height; + wgpu_surface_width = width; + wgpu_surface_height = height; WGPUSwapChainDescriptor swap_chain_desc = {}; swap_chain_desc.usage = WGPUTextureUsage_RenderAttachment; swap_chain_desc.format = wgpu_preferred_fmt; From 00251c8921bbc08092e472ad40ffedbca81e00db Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Oct 2025 12:01:10 +0200 Subject: [PATCH 09/13] CI: disable PVS-Studio's --disableLicenseExpirationCheck --- .github/workflows/static-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index a69c5110c..53db04764 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -42,5 +42,5 @@ jobs: fi cd examples/example_null pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1 - pvs-studio-analyzer analyze --disableLicenseExpirationCheck -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log + pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log From 9be4f150ef4c4561eaf8b4abc7ac34ccf2278995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Tassoux?= Date: Fri, 10 Oct 2025 17:56:31 +0200 Subject: [PATCH 10/13] Backends: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002) --- backends/imgui_impl_dx12.cpp | 84 ++++++++++++++++++++++-------------- docs/CHANGELOG.txt | 2 + 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 1e122a76e..d4da6ab02 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002) // 2025-09-29: DirectX12: Rework synchronization logic. (#8961) // 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965) // 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963) @@ -108,6 +109,9 @@ struct ImGui_ImplDX12_Data ID3D12CommandAllocator* pTexCmdAllocator; ID3D12GraphicsCommandList* pTexCmdList; + ID3D12Resource* pTexUploadBuffer; + UINT pTexUploadBufferSize; + void* pTexUploadBufferMapped; ImGui_ImplDX12_RenderBuffers* pFrameResources; UINT frameIndex; @@ -438,44 +442,53 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) UINT upload_pitch_dst = (upload_pitch_src + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); UINT upload_size = upload_pitch_dst * upload_h; - D3D12_RESOURCE_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Alignment = 0; - desc.Width = upload_size; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; + if (bd->pTexUploadBuffer == nullptr || upload_size > bd->pTexUploadBufferSize) + { + if (bd->pTexUploadBufferMapped) + { + D3D12_RANGE range = { 0, bd->pTexUploadBufferSize }; + bd->pTexUploadBuffer->Unmap(0, &range); + bd->pTexUploadBufferMapped = nullptr; + } + SafeRelease(bd->pTexUploadBuffer); - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + D3D12_RESOURCE_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + desc.Alignment = 0; + desc.Width = upload_size; + desc.Height = 1; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; - // FIXME-OPT: Could upload buffer be kept around, reused, and grown only when needed? Would that be worth it? - ID3D12Resource* uploadBuffer = nullptr; - HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)); - IM_ASSERT(SUCCEEDED(hr)); + D3D12_HEAP_PROPERTIES props; + memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + props.Type = D3D12_HEAP_TYPE_UPLOAD; + props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bd->pTexUploadBuffer)); + IM_ASSERT(SUCCEEDED(hr)); + + D3D12_RANGE range = {0, upload_size}; + hr = bd->pTexUploadBuffer->Map(0, &range, &bd->pTexUploadBufferMapped); + IM_ASSERT(SUCCEEDED(hr)); + bd->pTexUploadBufferSize = upload_size; + } bd->pTexCmdAllocator->Reset(); bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr); ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList; // Copy to upload buffer - void* mapped = nullptr; - D3D12_RANGE range = { 0, upload_size }; - hr = uploadBuffer->Map(0, &range, &mapped); - IM_ASSERT(SUCCEEDED(hr)); for (int y = 0; y < upload_h; y++) - memcpy((void*)((uintptr_t)mapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src); - uploadBuffer->Unmap(0, &range); + memcpy((void*)((uintptr_t)bd->pTexUploadBufferMapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src); if (need_barrier_before_copy) { @@ -492,7 +505,7 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; { - srcLocation.pResource = uploadBuffer; + srcLocation.pResource = bd->pTexUploadBuffer; srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; srcLocation.PlacedFootprint.Footprint.Width = upload_w; @@ -516,9 +529,8 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) cmdList->ResourceBarrier(1, &barrier); } - hr = cmdList->Close(); + HRESULT hr = cmdList->Close(); IM_ASSERT(SUCCEEDED(hr)); - ID3D12CommandQueue* cmdQueue = bd->pCommandQueue; cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue); @@ -531,7 +543,6 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent); ::WaitForSingleObject(bd->FenceEvent, INFINITE); - uploadBuffer->Release(); tex->SetStatus(ImTextureStatus_OK); } @@ -804,6 +815,13 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() bd->commandQueueOwned = false; SafeRelease(bd->pRootSignature); SafeRelease(bd->pPipelineState); + if (bd->pTexUploadBufferMapped) + { + D3D12_RANGE range = { 0, bd->pTexUploadBufferSize }; + bd->pTexUploadBuffer->Unmap(0, &range); + bd->pTexUploadBufferMapped = nullptr; + } + SafeRelease(bd->pTexUploadBuffer); SafeRelease(bd->pTexCmdList); SafeRelease(bd->pTexCmdAllocator); SafeRelease(bd->Fence); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9c5b50988..e72b81462 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,8 @@ Other Changes: of recreating them each time. (#8963, #8465) [@RT2Code] - Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code] (presumably fixes old hard-to-repro crash issues such as #3463, #5018) +- Backends: DirectX12: Reuse texture upload buffer and grow it only when + necessary. (#9002) [@RT2Code] - Backends: DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] - Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] From 878c863af4cdef451d9da92df4a3078694574411 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Oct 2025 13:25:02 +0200 Subject: [PATCH 11/13] Docs: reformat changlog entries. --- docs/CHANGELOG.txt | 135 +++++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e72b81462..409bcb11d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,50 +41,53 @@ HOW TO UPDATE? Breaking Changes: -- Backends: Vulkan: moved some fields in ImGui_ImplVulkan_InitInfo: - init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass - init_info.Subpass --> init_info.PipelineInfoMain.Subpass - init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples - init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo - It makes things more consistent and was desirable to introduce new settings for - secondary viewports. (#8946, #8110, #8111, #8686) [@ocornut, @SuperRonan, @sylmroz] -- Backends: Vulkan: renamed ImGui_ImplVulkan_MainPipelineCreateInfo --> ImGui_ImplVulkan_PipelineInfo - (introduced very recently and only used by `ImGui_ImplVulkan_CreateMainPipeline()` - so it should not affect many users). (#8110, #8111) -- Backends: Vulkan: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a - `VkImageUsageFlags image_usage` argument. - It was previously hardcoded to `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT` and defaults - to that when the value is 0. In theory the function is an internal helper but - since it's used by our examples some may have used it. (#8946, #8111, #8686) +- Backends: + - Vulkan: moved some fields in ImGui_ImplVulkan_InitInfo: + init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass + init_info.Subpass --> init_info.PipelineInfoMain.Subpass + init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples + init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo + It makes things more consistent and was desirable to introduce new settings for + secondary viewports. (#8946, #8110, #8111, #8686) [@ocornut, @SuperRonan, @sylmroz] + - Vulkan: renamed ImGui_ImplVulkan_MainPipelineCreateInfo --> ImGui_ImplVulkan_PipelineInfo + (introduced very recently and only used by `ImGui_ImplVulkan_CreateMainPipeline()` + so it should not affect many users). (#8110, #8111) + - Vulkan: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a + `VkImageUsageFlags image_usage` argument. + It was previously hardcoded to `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT` and defaults + to that when the value is 0. In theory the function is an internal helper but + since it's used by our examples some may have used it. (#8946, #8111, #8686) Other Changes: - Windows: added lower-right resize grip on child windows using both ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY flags. (#8501) [@aleksijuvani] The grip is not visible before hovering to reduce clutter. -- InputText: fixed single-line InputText() not applying fine character clipping - properly (regression in 1.92.3). (#8967) [@Cyphall] -- InputText: fixed an infinite loop error happening if a custom input text - callback modifies/clear BufTextLen before calling InsertChars(). - (regression from 1.92.3). Note that this never really worked correctly, but - previously it would only temporary wreck cursor position, and since 1.92.3 it - would go in an infinite loop. (#8994, #3237) - Style: added ImGuiCol_UnsavedMarker, color of the unsaved document marker when using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) -- Textures: fixed a crash if texture status is set to _WantDestroy by a backend after - it had already been destroyed. This would typically happen when calling backend's - ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811) -- Textures: allowed backend to destroy texture while inside the NewFrame/EndFrame - scope. Basically if a backend decide to destroy a texture that we didn't request - to destroy (for e.g. freeing resources) the texture is immediately set to - a _WantCreate status again. (#8811) -- Textures: fixed an issue preventing multi-contexts sharing a ImFontAtlas from - being possible to destroy in any order. -- Textures: fixed not updating ImTextureData's RefCount when destroying a context - using a shared ImFontAtlas, leading standard backends to not properly free - texture resources. (#8975) [@icrashstuff] +- InputText: + - Fixed single-line InputText() not applying fine character clipping + properly (regression in 1.92.3). (#8967) [@Cyphall] + - Fixed an infinite loop error happening if a custom input text + callback modifies/clear BufTextLen before calling InsertChars(). + (regression from 1.92.3). Note that this never really worked correctly, but + previously it would only temporary wreck cursor position, and since 1.92.3 it + would go in an infinite loop. (#8994, #3237) +- Textures: + - Fixed a crash if texture status is set to _WantDestroy by a backend after + it had already been destroyed. This would typically happen when calling backend's + ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811) + - Allowed backend to destroy texture while inside the NewFrame/EndFrame + scope. Basically if a backend decide to destroy a texture that we didn't request + to destroy (for e.g. freeing resources) the texture is immediately set to + a _WantCreate status again. (#8811) + - Fixed an issue preventing multi-contexts sharing a ImFontAtlas from + being possible to destroy in any order. + - Fixed not updating ImTextureData's RefCount when destroying a context + using a shared ImFontAtlas, leading standard backends to not properly + free texture resources. (#8975) [@icrashstuff] - Demo: fixed layout issue in "Layout & Scrolling -> Scrolling" section. - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc. @@ -92,37 +95,39 @@ Other Changes: - CI: updated Windows CI scripts to generate/use VulkanSDK. (#8925, #8778) [@yaz0r] - Docs: updated FAQ with new "What is the difference between Dear ImGui and traditional UI toolkits?" entry. (#8862) -- Backends: all backends call ImGuiPlatformIO::ClearPlatformHandlers() and - ClearRendererHandlers() on shutdown, so as not to leave function pointers - which may be dangling when using backend in e.g. DLL. (#8945, #2769) -- Backends: DirectX12: reuse a command list and allocator for texture uploads instead - of recreating them each time. (#8963, #8465) [@RT2Code] -- Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code] - (presumably fixes old hard-to-repro crash issues such as #3463, #5018) -- Backends: DirectX12: Reuse texture upload buffer and grow it only when - necessary. (#9002) [@RT2Code] -- Backends: DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] -- Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support - `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] -- Backends: GLFW: fixed build on platform that are neither Windows, macOS or - known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion] -- Backends: SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our - window is hovered, as the event data is reliable and enough in this case. - - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786) - - Essentially a working for SDL3 bug which will be fixed in SDL 3.3.0. - - Better perf on X11 as querying global position requires a round trip to X11 server. -- Backends: Win32: minor optimization not submitting gamepad io again if - XInput's dwPacketNumber has not changed. (#8556) [@MidTerm-CN] -- Backends: Vulkan: added a way to specify custom shaders by filling init fields - CustomShaderVertCreateInfo and CustomShaderFragCreateInfo. (#8585, #8271) [@johan0A] -- Backends: DX9,DX10,DX11,DX12,Metal,Vulkan,WGPU,SDLRenderer2,SDLRenderer3: - ensure that a texture in _WantDestroy state always turn to _Destroyed even - if your underlying graphics data was already destroyed. (#8977) -- Examples: SDL2+DirectX11: Try WARP software driver if hardware driver is - not available. (#5924, #5562) -- Examples: SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] -- Examples: Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] -- Examples: made examples's main.cpp consistent with returning 1 on error. +- Backends: + - All backends call ImGuiPlatformIO::ClearPlatformHandlers() and + ClearRendererHandlers() on shutdown, so as not to leave function pointers + which may be dangling when using backend in e.g. DLL. (#8945, #2769) + - DirectX12: reuse a command list and allocator for texture uploads instead + of recreating them each time. (#8963, #8465) [@RT2Code] + - DirectX12: Rework synchronization logic. (#8961) [@RT2Code] + (presumably fixes old hard-to-repro crash issues such as #3463, #5018) + - DirectX12: Reuse texture upload buffer and grow it only when + necessary. (#9002) [@RT2Code] + - DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] + - OpenGL3: fixed GL loader to work on Haiku OS which does not support + `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] + - GLFW: fixed build on platform that are neither Windows, macOS or + known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion] + - SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our + window is hovered, as the event data is reliable and enough in this case. + - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786) + - Essentially a workaround for SDL3 bug which will be fixed in SDL 3.3.0. + - Better perf on X11 as querying global position requires a round trip to X11 server. + - Win32: minor optimization not submitting gamepad io again if + XInput's dwPacketNumber has not changed. (#8556) [@MidTerm-CN] + - Vulkan: added a way to specify custom shaders by filling init fields + CustomShaderVertCreateInfo and CustomShaderFragCreateInfo. (#8585, #8271) [@johan0A] + - DX9,DX10,DX11,DX12,Metal,Vulkan,WGPU,SDLRenderer2,SDLRenderer3: + ensure that a texture in _WantDestroy state always turn to _Destroyed even + if your underlying graphics data was already destroyed. (#8977) +- Examples: + - SDL2+DirectX11: Try WARP software driver if hardware driver is + not available. (#5924, #5562) + - SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] + - Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] + - Made examples's main.cpp consistent with returning 1 on error. ----------------------------------------------------------------------- From b6e277980fbb00317ef5a9ff319e64ac3976866c Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Oct 2025 15:02:55 +0200 Subject: [PATCH 12/13] Shortcuts: reorganize route scoring so values are easier to read. (#9004) Score now require 16-bits but ImGuiKeyRoutingData doesn't grow size. --- imgui.cpp | 49 +++++++++++++++++++++++++----------------------- imgui_internal.h | 6 +++--- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2ba3aacd9..484c8513f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9378,7 +9378,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner; - routing_entry->RoutingNextScore = 255; + routing_entry->RoutingNextScore = 0; if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner) continue; rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer @@ -9447,23 +9447,24 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) return routing_data; } -// Current score encoding (lower is highest priority): -// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive -// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active) -// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused -// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack) -// - 254: ImGuiInputFlags_RouteGlobal -// - 255: never route +// Current score encoding +// - 0: Never route +// - 1: ImGuiInputFlags_RouteGlobal (lower priority) +// - 100..199: ImGuiInputFlags_RouteFocused (if window in focus-stack) +// 200: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused +// 300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active) +// 400: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive +// - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority) // 'flags' should include an explicit routing policy static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; if (flags & ImGuiInputFlags_RouteFocused) { - // ActiveID gets top priority + // ActiveID gets high priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) if (owner_id != 0 && g.ActiveId == owner_id) - return 1; + return 300; // Score based on distance to focused window (lower is better) // Assuming both windows are submitting a routing request, @@ -9473,25 +9474,27 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. // This essentially follow the window->ParentWindowForFocusRoute chain. if (focus_scope_id == 0) - return 255; + return 0; for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) - return 3 + index_in_focus_path; - return 255; + return 199 - index_in_focus_path; + return 0; } else if (flags & ImGuiInputFlags_RouteActive) { if (owner_id != 0 && g.ActiveId == owner_id) - return 1; - return 255; + return 300; + return 0; } else if (flags & ImGuiInputFlags_RouteGlobal) { if (flags & ImGuiInputFlags_RouteOverActive) - return 0; + return 400; + if (owner_id != 0 && g.ActiveId == owner_id) + return 300; if (flags & ImGuiInputFlags_RouteOverFocused) - return 2; - return 254; + return 200; + return 1; } IM_ASSERT(0); return 0; @@ -9587,17 +9590,17 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I const int score = CalcRoutingScore(focus_scope_id, owner_id, flags); IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score); - if (score == 255) + if (score == 0) return false; // Submit routing for NEXT frame (assuming score is sufficient) - // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <). + // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >). ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); - //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore); - if (score < routing_data->RoutingNextScore) + //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore); + if (score > routing_data->RoutingNextScore) { routing_data->RoutingNext = owner_id; - routing_data->RoutingNextScore = (ImU8)score; + routing_data->RoutingNextScore = (ImU16)score; } // Return routing state for CURRENT frame diff --git a/imgui_internal.h b/imgui_internal.h index 1510271a0..c9c1f1333 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1556,12 +1556,12 @@ struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. - ImU8 RoutingCurrScore; // [DEBUG] For debug display - ImU8 RoutingNextScore; // Lower is better (0: perfect score) + ImU16 RoutingCurrScore; // [DEBUG] For debug display + ImU16 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; - ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. From bad5ee167b06484e06015bca34ebfb20f8aeed24 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 13 Oct 2025 15:03:26 +0200 Subject: [PATCH 13/13] Shortcuts: added support for combining ImGuiInputFlags_RouteFocused with ImGuiInputFlags_RouteOverActive,. (#9004) --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 11 +++++++++-- imgui.h | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 409bcb11d..73c029ae0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,10 @@ Other Changes: using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) +- Inputs: + - Shortcuts: added support for combining ImGuiInputFlags_RouteFocused + (which is the default route) with ImGuiInputFlags_RouteOverActive, allowing + to steal shortcuts from active item without using global routing. (#9004) - InputText: - Fixed single-line InputText() not applying fine character clipping properly (regression in 1.92.3). (#8967) [@Cyphall] diff --git a/imgui.cpp b/imgui.cpp index 484c8513f..2cf1cb273 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -9477,7 +9477,12 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput return 0; for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) - return 199 - index_in_focus_path; + { + if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id) + return 599 - index_in_focus_path; + else + return 199 - index_in_focus_path; + } return 0; } else if (flags & ImGuiInputFlags_RouteActive) @@ -9534,8 +9539,10 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I else IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner); - if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused)) + if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused)) IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal); + if (flags & ImGuiInputFlags_RouteOverActive) + IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused)); // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. key_chord = FixupKeyChord(key_chord); diff --git a/imgui.h b/imgui.h index 24c6423b4..b798903be 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.4 WIP" -#define IMGUI_VERSION_NUM 19235 +#define IMGUI_VERSION_NUM 19236 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198