From 43a90adc618207da0e9ef6274f1b172c76f220ae Mon Sep 17 00:00:00 2001 From: maf Date: Thu, 8 Jan 2026 16:17:44 +0800 Subject: [PATCH 01/15] Docs: Fix spelling errors (#9158) --- imgui.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 2df755416..8d6b63719 100644 --- a/imgui.h +++ b/imgui.h @@ -1539,7 +1539,7 @@ enum ImGuiSortDirection : ImU8 // All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87). // Support for legacy keys was completely removed in 1.91.5. // Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921 -// Note that "Keys" related to physical keys and are not the same concept as input "Characters", the later are submitted via io.AddInputCharacter(). +// Note that "Keys" related to physical keys and are not the same concept as input "Characters", the latter are submitted via io.AddInputCharacter(). // The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps. enum ImGuiKey : int { @@ -2438,7 +2438,7 @@ struct ImGuiIO // Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL] // - Error recovery is provided as a way to facilitate: - // - Recovery after a programming error (native code or scripting language - the later tends to facilitate iterating on code while running). + // - Recovery after a programming error (native code or scripting language - the latter tends to facilitate iterating on code while running). // - Recovery after running an exception handler or any error processing which may skip code after an error has been detected. // - Error recovery is not perfect nor guaranteed! It is a feature to ease development. // You not are not supposed to rely on it in the course of a normal application run. From 9ce41a92c34ec0170247dfd09c0c48ae92e19109 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 14:31:25 +0100 Subject: [PATCH 02/15] Misc/shallow merges from docking branch to reduce small drift. --- backends/imgui_impl_glfw.cpp | 6 +++--- backends/imgui_impl_glfw.h | 4 ++-- backends/imgui_impl_metal.mm | 2 +- backends/imgui_impl_osx.mm | 12 +++++++----- backends/imgui_impl_sdl2.cpp | 6 +++--- backends/imgui_impl_sdl3.cpp | 10 +++------- backends/imgui_impl_vulkan.cpp | 2 +- backends/imgui_impl_vulkan.h | 1 - backends/imgui_impl_win32.cpp | 6 +++--- examples/example_glfw_opengl2/Makefile | 3 ++- imgui_widgets.cpp | 2 +- 11 files changed, 26 insertions(+), 28 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 35b167b28..82c512fee 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -11,8 +11,8 @@ // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Multiple Dear ImGui contexts support. // Missing features or Issues: -// [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. -// [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. +// [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. +// [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -156,13 +156,13 @@ // We gather version tests as define in order to easily see which features are version-dependent. #define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) +#define GLFW_HAS_CREATECURSOR (GLFW_VERSION_COMBINED >= 3100) // 3.1+ glfwCreateCursor() #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale #ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else #define GLFW_HAS_NEW_CURSORS (0) #endif -#define GLFW_HAS_CREATECURSOR (GLFW_VERSION_COMBINED >= 3100) // 3.1+ glfwCreateCursor() #define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api #define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() #define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 98fae51c7..c01600d0c 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -11,8 +11,8 @@ // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors) with GLFW 3.1+. Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Multiple Dear ImGui contexts support. // Missing features or Issues: -// [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. -// [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. +// [ ] Platform: Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. +// [ ] Platform: Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 2746efa95..96695d9b7 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -19,7 +19,7 @@ // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplMetal_CreateFontsTexture() and ImGui_ImplMetal_DestroyFontsTexture(). // 2025-02-03: Metal: Crash fix. (#8367) -// 2024-01-08: Metal: Fixed memory leaks when using metal-cpp (#8276, #8166) or when using multiple contexts (#7419). +// 2025-01-08: Metal: Fixed memory leaks when using metal-cpp (#8276, #8166) or when using multiple contexts (#7419). // 2022-08-23: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. // 2022-07-05: Metal: Add dispatch synchronization. // 2022-06-30: Metal: Use __bridge for ARC based systems. diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 189dd53e6..0dfdd8e90 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -148,9 +148,11 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view); NSWindow* window = view.window; if (!window) return; - NSRect contentRect = [window contentRectForFrameRect:window.frame]; - NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); - _imeRect = [window convertRectToScreen:rect]; + { + NSRect contentRect = [window contentRectForFrameRect:window.frame]; + NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); + _imeRect = [window convertRectToScreen:rect]; + } } - (void)viewDidMoveToWindow @@ -629,9 +631,9 @@ void ImGui_ImplOSX_NewFrame(NSView* view) // Setup display size if (view) { - const float dpi = (float)[view.window backingScaleFactor]; + const float fb_scale = (float)[view.window backingScaleFactor]; io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height); - io.DisplayFramebufferScale = ImVec2(dpi, dpi); + io.DisplayFramebufferScale = ImVec2(fb_scale, fb_scale); } // Setup time step diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index c2dae4053..371b1dac4 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -140,7 +140,7 @@ struct ImGui_ImplSDL2_Data { SDL_Window* Window; - Uint32 WindowID; + Uint32 WindowID; // Stored in ImGuiViewport::PlatformHandle. Use SDL_GetWindowFromID() to get SDL_Window* from Uint32 WindowID. SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; @@ -368,7 +368,6 @@ static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -462,7 +461,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) bd->MouseLastLeaveFrame = ImGui::GetFrameCount() + 1; if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); - else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) + if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) io.AddFocusEvent(false); return true; } @@ -487,6 +486,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void ImGuiIO& io = ImGui::GetIO(); IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + //SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2"); // Obtain compiled and runtime versions SDL_version ver_compiled; diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 815929595..ffd9f8a59 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -372,7 +372,6 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -495,6 +494,7 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void IMGUI_CHECKVERSION(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); IM_UNUSED(sdl_gl_context); // Unused in this branch + //SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2"); const int ver_linked = SDL_GetVersion(); @@ -555,22 +555,18 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered. // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: - // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) -#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH + // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_EVENT_WINDOW_FOCUS_GAINED) SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); -#endif // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) -#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); -#endif return true; } +// Should technically be a SDL_GLContext but due to typedef it is sane to keep it void* in public interface. bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { - IM_UNUSED(sdl_gl_context); // Viewport branch will need this. return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); } diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 2386f0bdf..f0ca7907a 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -261,7 +261,7 @@ struct ImGui_ImplVulkan_Data VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; VkPipelineLayout PipelineLayout; - VkPipeline Pipeline; + VkPipeline Pipeline; // pipeline for main render pass (created by app) VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; VkDescriptorPool DescriptorPool; diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index c6cd72cfd..83376e597 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -77,7 +77,6 @@ // Specify settings to create pipeline and swapchain struct ImGui_ImplVulkan_PipelineInfo { - // For Main and Secondary viewports VkRenderPass RenderPass; // Ignored if using dynamic rendering uint32_t Subpass; // VkSampleCountFlagBits MSAASamples = {}; // 0 defaults to VK_SAMPLE_COUNT_1_BIT diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index c3b7ca9cc..de3c6fbd6 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -181,7 +181,7 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) bd->LastMouseCursor = ImGuiMouseCursor_COUNT; ImGui_ImplWin32_UpdateKeyboardCodePage(io); - // Set platform dependent data in viewport + // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd; IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch @@ -650,8 +650,8 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA bd->MouseTrackedArea = area; } POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) }; - if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates. - return 0; + if (msg == WM_NCMOUSEMOVE) // WM_NCMOUSEMOVE are absolute coordinates. + ::ScreenToClient(hwnd, &mouse_pos); io.AddMouseSourceEvent(mouse_source); io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); return 0; diff --git a/examples/example_glfw_opengl2/Makefile b/examples/example_glfw_opengl2/Makefile index 1f15c15c0..a8a0128dc 100644 --- a/examples/example_glfw_opengl2/Makefile +++ b/examples/example_glfw_opengl2/Makefile @@ -21,6 +21,7 @@ SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl2.cpp OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) UNAME_S := $(shell uname -s) +LINUX_GL_LIBS = -lGL CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends CXXFLAGS += -g -Wall -Wformat @@ -32,7 +33,7 @@ LIBS = ifeq ($(UNAME_S), Linux) #LINUX ECHO_MESSAGE = "Linux" - LIBS += -lGL `pkg-config --static --libs glfw3` + LIBS += $(LINUX_GL_LIBS) `pkg-config --static --libs glfw3` CXXFLAGS += `pkg-config --cflags glfw3` CFLAGS = $(CXXFLAGS) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fac10d832..83e931a0e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1720,7 +1720,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); - const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; From 3389dfd9dd9edf4d6f5884d9d1848446dce87689 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 15:58:12 +0100 Subject: [PATCH 03/15] IsItemHovered() doesn't filter out the signal when activated item is a shortcut remote activation. (#9138, #456) Amend a201af7354 --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2cb0d5d94..521ec6b53 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -98,7 +98,6 @@ Breaking Changes: - BeginPopupContextItem("foo", 0); // !! Behavior changed !! Was Left button. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft. - BeginPopupContextItem("foo", ImGuiPopupFlags_NoReopen); // !! Behavior changed !! Was Left button + flags. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft | xxx. - Other Changes: - Fonts: @@ -155,6 +154,9 @@ Other Changes: noticeable by user but detected by sanitizers). (#9089) [@judicaelclair] - Added GetItemFlags() in public API for consistency and to expose generic flags of last submitted item. (#9127) +- IsItemHovered() without ImGuiHoveredFlags_AllowWhenBlockedByActiveItem + doesn't filter out the signal when activated item is a shortcut remote activation; + (which mimicks what's done internally in the ItemHoverable() function). (#9138) - Debug Tools: - Debug Log: fixed incorrectly printing characters in IO log when submitting non-ASCII values to io.AddInputCharacter(). (#9099) diff --git a/imgui.cpp b/imgui.cpp index da9a7019b..996d162b3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4824,7 +4824,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if another item is active (e.g. being dragged) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && !g.ActiveIdFromShortcut) { // When ActiveId == MoveId it means that either: // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag) @@ -13571,6 +13571,7 @@ static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource() ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; + //const bool activated_shortcut = false // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut) @@ -13586,6 +13587,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() ImGuiInputSource source = NavCalcPreferredRefPosSource(); const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; + //const bool activated_shortcut = false if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL) source = ImGuiInputSource_Mouse; From 1566c96ccd5faa7fddf34329687ac16796bebabc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 17:04:52 +0100 Subject: [PATCH 04/15] InputText, Nav: fixed remote/shortcut InputText() not teleporting mouse cursor when nav cursor is active and io.ConfigNavMoveSetMousePos is enabled. Motivated by this, also made SetFocusID() immediately set g.NavIdIsAlive, which is more correct and might be other (positive) subtle side effects. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 2 ++ imgui_widgets.cpp | 2 ++ 3 files changed, 7 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 521ec6b53..3a2bea6e2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -150,6 +150,9 @@ Other Changes: end of a line rather than at the beginning of next line. (#8990, #3237) - Fixed low-level word-wrapping function reading from *text_end when passed a string range. (#9107) [@achabense] +- Nav: + - Fixed remote/shortcut InputText() not teleporting mouse cursor when + nav cursor is visible and `io.ConfigNavMoveSetMousePos` is enabled. - Scrollbar: fixed a codepath leading to a divide-by-zero (which would not be noticeable by user but detected by sanitizers). (#9089) [@judicaelclair] - Added GetItemFlags() in public API for consistency and to expose generic diff --git a/imgui.cpp b/imgui.cpp index 996d162b3..4e59a1a6c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13055,6 +13055,8 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); + if (g.NavId == g.ActiveId && g.ActiveIdIsAlive) + g.NavIdIsAlive = true; if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) g.NavHighlightItemUnderNav = true; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 83e931a0e..88d3b7ca4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4848,6 +4848,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); + if (input_requested_by_nav) + SetNavCursorVisibleAfterMove(); } if (g.ActiveId == id) { From ca9b7b4071f89457c0e01919caade8b6c59d352d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 17:11:23 +0100 Subject: [PATCH 05/15] InputText, Nav: amend 1566c96. (incorrectly commited old chunk) --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 4e59a1a6c..d4bd9adb3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -13055,7 +13055,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); - if (g.NavId == g.ActiveId && g.ActiveIdIsAlive) + if (id == g.ActiveIdIsAlive) g.NavIdIsAlive = true; if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) From 7a02f4b545e719a00ddb8b15d35bf6fbd78c301e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 17:14:38 +0100 Subject: [PATCH 06/15] Nav, Shortcuts, Tooltips: tooltip reference position not affected by remote shortcut activation. (#9138, #456) NavCalcPreferredRefPos() has different path for popups vs tooltip. Amend 197f8904fe --- imgui.cpp | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d4bd9adb3..e4a63f9c7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1347,8 +1347,8 @@ static bool NavScoreItem(ImGuiNavItemData* result, const ImRect& nav static void NavApplyItemToResult(ImGuiNavItemData* result); static void NavProcessItem(); static void NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags); -static ImGuiInputSource NavCalcPreferredRefPosSource(); -static ImVec2 NavCalcPreferredRefPos(); +static ImGuiInputSource NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type); +static ImVec2 NavCalcPreferredRefPos(ImGuiWindowFlags window_type); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); @@ -12203,7 +12203,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) popup_ref.RestoreNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type). popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); + popup_ref.OpenPopupPos = NavCalcPreferredRefPos(ImGuiWindowFlags_Popup); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id); @@ -12681,9 +12681,9 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin(). IM_ASSERT(g.CurrentWindow == window); const float scale = g.Style.MouseCursorScale; - const ImVec2 ref_pos = NavCalcPreferredRefPos(); + const ImVec2 ref_pos = NavCalcPreferredRefPos(ImGuiWindowFlags_Tooltip); - if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse) + if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource(ImGuiWindowFlags_Tooltip) == ImGuiInputSource_Mouse) { ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size); if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size))) @@ -13568,33 +13568,29 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) } } -static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource() +// Positioning logic altered slightly for remote activation: for Popup we want to use item rect, for Tooltip we leave things alone. (#9138) +// When calling for ImGuiWindowFlags_Popup we use LastItemData. +static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource(ImGuiWindowFlags window_type) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; - const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; - //const bool activated_shortcut = false - // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. - if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut) + const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; + if ((window_type & ImGuiWindowFlags_Popup) && activated_shortcut) + return ImGuiInputSource_Keyboard; + + if (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) return ImGuiInputSource_Mouse; else return ImGuiInputSource_Keyboard; // or Nav in general } -static ImVec2 ImGui::NavCalcPreferredRefPos() +static ImVec2 ImGui::NavCalcPreferredRefPos(ImGuiWindowFlags window_type) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.NavWindow; - ImGuiInputSource source = NavCalcPreferredRefPosSource(); + ImGuiInputSource source = NavCalcPreferredRefPosSource(window_type); - const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; - //const bool activated_shortcut = false - - if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL) - source = ImGuiInputSource_Mouse; - - // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag. if (source == ImGuiInputSource_Mouse) { // Mouse (we need a fallback in case the mouse becomes invalid after being used) @@ -13606,8 +13602,9 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() else { // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item + const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID; ImRect ref_rect; - if (activated_shortcut) + if (activated_shortcut && (window_type & ImGuiWindowFlags_Popup)) ref_rect = g.LastItemData.NavRect; else if (window != NULL) ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]); @@ -13806,7 +13803,7 @@ static void ImGui::NavUpdate() // Update mouse position if requested // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - TeleportMousePos(NavCalcPreferredRefPos()); + TeleportMousePos(NavCalcPreferredRefPos(ImGuiWindowFlags_Popup)); // [DEBUG] g.NavScoringDebugCount = 0; From fedf227ea4f0bffbc75ff3d3de6631a4c57a4ba1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 17:17:06 +0100 Subject: [PATCH 07/15] Amend 7a02f4b. --- docs/CHANGELOG.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3a2bea6e2..c3ef808a3 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -157,9 +157,12 @@ Other Changes: noticeable by user but detected by sanitizers). (#9089) [@judicaelclair] - Added GetItemFlags() in public API for consistency and to expose generic flags of last submitted item. (#9127) -- IsItemHovered() without ImGuiHoveredFlags_AllowWhenBlockedByActiveItem - doesn't filter out the signal when activated item is a shortcut remote activation; - (which mimicks what's done internally in the ItemHoverable() function). (#9138) +- Shortcuts: + - IsItemHovered() without ImGuiHoveredFlags_AllowWhenBlockedByActiveItem + doesn't filter out the signal when activated item is a shortcut remote activation; + (which mimicks what's done internally in the ItemHoverable() function). (#9138) + - Fixed tooltip placement being affected for a frame when located over an item + activated by SetNextItemShortcut(). (#9138) - Debug Tools: - Debug Log: fixed incorrectly printing characters in IO log when submitting non-ASCII values to io.AddInputCharacter(). (#9099) From f5384544cb350dd2444c27c153ac563b91cf09da Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 8 Jan 2026 17:26:25 +0100 Subject: [PATCH 08/15] (Breaking) Commented out legacy names obsoleted in 1.90 (Sept 2023): BeginChildFrame(), EndChildFrame(), ShowStackToolWindow(), IM_OFFSETOF(), IM_FLOOR(). --- docs/CHANGELOG.txt | 6 ++++++ imgui.cpp | 7 +++---- imgui.h | 13 +++++++------ imgui_internal.h | 6 ++---- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c3ef808a3..b7b4f2cd9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,12 @@ HOW TO UPDATE? Breaking Changes: +- Commented out legacy names obsoleted in 1.90 (Sept 2023): + - BeginChildFrame() --> BeginChild() with ImGuiChildFlags_FrameStyle flag. + - EndChildFrame() --> EndChild(). + - ShowStackToolWindow() --> ShowIDStackToolWindow(). + - IM_OFFSETOF() --> offsetof(). + - IM_FLOOR() --> IM_TRUNC() [internal, for positive values only] - Hashing: handling of "###" operator to reset to seed within a string identifier doesn't include the "###" characters in the output hash anymore: Before: GetID("Hello###World") == GetID("###World") != GetID("World"); diff --git a/imgui.cpp b/imgui.cpp index e4a63f9c7..b1948d665 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -394,6 +394,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/01/08 (1.92.6) - Commented out legacy names obsoleted in 1.90 (Sept 2023): 'BeginChildFrame()' --> 'BeginChild()' with 'ImGuiChildFlags_FrameStyle'. 'EndChildFrame()' --> 'EndChild()'. 'ShowStackToolWindow()' --> 'ShowIDStackToolWindow()'. 'IM_OFFSETOF()' --> 'offsetof()'. - 2026/01/07 (1.92.6) - Popups: changed compile-time 'ImGuiPopupFlags popup_flags = 1' default value to be '= 0' for BeginPopupContextItem(), BeginPopupContextWindow(), BeginPopupContextVoid(), OpenPopupOnItemClick(). Default value has same meaning before and after. - Refer to GitHub topic #9157 if you have any question. - Before this version, those functions had a 'ImGuiPopupFlags popup_flags = 1' default value in their function signature. @@ -6386,10 +6387,8 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!"); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) - // child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; - //if (window_flags & ImGuiWindowFlags_NavFlattened) - // child_flags |= ImGuiChildFlags_NavFlattened; + //if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) { child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; } + //if (window_flags & ImGuiWindowFlags_NavFlattened) { child_flags |= ImGuiChildFlags_NavFlattened; } #endif if (child_flags & ImGuiChildFlags_AutoResizeX) child_flags &= ~ImGuiChildFlags_ResizeX; diff --git a/imgui.h b/imgui.h index 8d6b63719..fb2a505af 100644 --- a/imgui.h +++ b/imgui.h @@ -4035,15 +4035,16 @@ namespace ImGui IMGUI_API ImVec2 GetWindowContentRegionMin(); // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! IMGUI_API ImVec2 GetWindowContentRegionMax(); // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()! // OBSOLETED in 1.90.0 (from September 2023) - inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } - inline void EndChildFrame() { EndChild(); } - //inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders - //inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders - inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + // OBSOLETED in 1.90.0 (from September 2023) + //inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + //inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders + //inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, flags); } + //inline void EndChildFrame() { EndChild(); } + //inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } // OBSOLETED in 1.89.7 (from June 2023) //IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() _before_ item. //-- OBSOLETED in 1.89.4 (from March 2023) @@ -4153,7 +4154,7 @@ typedef ImFontAtlasRect ImFontAtlasCustomRect; //typedef ImGuiKeyChord ImGuiKeyModFlags; // == int //enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super }; -#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version) +//#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version) #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_internal.h b/imgui_internal.h index 86606f39f..6af67614e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -278,9 +278,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds #define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) -#endif +//#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) // Hint for branch prediction #if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L)) @@ -3784,7 +3782,7 @@ struct ImFontLoader IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype(); #endif #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are not actually compatible but we provide this as a compile-time error report helper. +typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92.0] The types are not actually compatible but we provide this as a compile-time error report helper. #endif //----------------------------------------------------------------------------- From d1c5a66557b35f8ea473d33cf363c1b0ec46050d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 12 Jan 2026 12:11:10 +0100 Subject: [PATCH 09/15] Backends: WebGPU: rework unsupported path into an assert. (#9155, #9156) --- backends/imgui_impl_wgpu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index c8722cd4a..e98e9f3ca 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -1078,7 +1078,7 @@ WGPUSurface ImGui_ImplWGPU_CreateWGPUSurfaceHelper(ImGui_ImplWGPU_CreateSurfaceI surface = wgpuInstanceCreateSurface(info->Instance, &surface_descriptor); } #else - fprintf(stderr, "'CreateWGPUSurfaceHelper' is not implemented for this platform\n"); + IM_ASSERT(0 && "Unsupported WebGPU native platform!"); #endif return surface; } From f64c7c37efd9b0cead78e84f9378398b68ae5f61 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 12 Jan 2026 18:17:38 +0100 Subject: [PATCH 10/15] Fonts: fixed a crash when trying to use AddFont() with MergeMode=true on a font that has already been rendered. (#9162) --- docs/CHANGELOG.txt | 2 ++ imgui_draw.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b7b4f2cd9..cd9315352 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -74,6 +74,8 @@ Breaking Changes: until a shutdown of the owning context or font atlas. - The fact that handling of `FontDataOwnedByAtlas = false` was broken bypassed the issue altogether. + - Fixed a crash when trying to use AddFont() with MergeMode=true on a font that + has already been rendered. (#9162) [@ocornut, @cyfewlp] - Removed ImFontConfig::PixelSnapV added in 1.92 which turns out is unnecessary (and misdocumented). Post-rescale GlyphOffset is always rounded. - Popups: changed compile-time 'ImGuiPopupFlags popup_flags = 1' default value to be '= 0' for diff --git a/imgui_draw.cpp b/imgui_draw.cpp index b839291b0..780f6b844 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3049,6 +3049,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) { IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back(); + ImFontAtlasFontDiscardBakes(this, font, 0); // Need to discard bakes if the font was already used, because baked->FontLoaderDatas[] will change size. (#9162) } // Add to list From 791ad9b82db44ada9fedb3e26b2d900974ac0959 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Jan 2026 16:15:48 +0100 Subject: [PATCH 11/15] InvisibleButton: allow calling with size (0,0) to fit to available content size. (#9166, #7623) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cd9315352..76d922194 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -163,6 +163,8 @@ Other Changes: nav cursor is visible and `io.ConfigNavMoveSetMousePos` is enabled. - Scrollbar: fixed a codepath leading to a divide-by-zero (which would not be noticeable by user but detected by sanitizers). (#9089) [@judicaelclair] +- InvisibleButton: allow calling with size (0,0) to fit to available content + size. (#9166, #7623) - Added GetItemFlags() in public API for consistency and to expose generic flags of last submitted item. (#9127) - Shortcuts: diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 88d3b7ca4..4244c0be3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -836,11 +836,10 @@ bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiBut if (window->SkipItems) return false; - // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size. - IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f); + // Ensure zero-size fits to contents + ImVec2 size = CalcItemSize(ImVec2(size_arg.x != 0.0f ? size_arg.x : -FLT_MIN, size_arg.y != 0.0f ? size_arg.y : -FLT_MIN), 0.0f, 0.0f); const ImGuiID id = window->GetID(str_id); - ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); ItemSize(size); if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav)) From 0d08927dae557cd537416661d932b19dc6f040db Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jan 2026 14:59:39 +0100 Subject: [PATCH 12/15] Error handling: Improve error handling and recovery for EndMenu()/EndCombo(). (#1651, #9165, #8499) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 2 +- imgui_widgets.cpp | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 76d922194..0645eff90 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -173,6 +173,8 @@ Other Changes: (which mimicks what's done internally in the ItemHoverable() function). (#9138) - Fixed tooltip placement being affected for a frame when located over an item activated by SetNextItemShortcut(). (#9138) +- Error Handling: + - Improve error handling and recovery for EndMenu()/EndCombo(). (#1651, #9165, #8499) - Debug Tools: - Debug Log: fixed incorrectly printing characters in IO log when submitting non-ASCII values to io.AddInputCharacter(). (#9099) diff --git a/imgui.cpp b/imgui.cpp index b1948d665..2a63f26e7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12455,7 +12455,7 @@ void ImGui::EndPopup() ImGuiWindow* window = g.CurrentWindow; if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || g.BeginPopupStack.Size == 0) { - IM_ASSERT_USER_ERROR(0, "Calling EndPopup() too many times or in wrong window!"); + IM_ASSERT_USER_ERROR(0, "Calling EndPopup() in wrong window!"); return; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 4244c0be3..3b6ee2156 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2053,8 +2053,15 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags void ImGui::EndCombo() { ImGuiContext& g = *GImGui; - EndPopup(); g.BeginComboDepth--; + char name[16]; + ImFormatString(name, IM_COUNTOF(name), "##Combo_%02d", g.BeginComboDepth); // FIXME: Move those to helpers? + if (strcmp(g.CurrentWindow->Name, name) != 0) + { + IM_ASSERT_USER_ERROR(0, "Calling EndCombo() in wrong window!"); + return; + } + EndPopup(); } // Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements @@ -9399,7 +9406,12 @@ void ImGui::EndMenu() // Nav: When a left move request our menu failed, close ourselves. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls + + if ((window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) != (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) + { + IM_ASSERT_USER_ERROR(0, "Calling EndMenu() in wrong window!"); + return; + } ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert. if (window->BeginCount == window->BeginCountPreviousFrame) if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet()) @@ -10343,7 +10355,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f ImGuiTabBar* tab_bar = g.CurrentTabBar; if (tab_bar == NULL) { - IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! From 7143d711bf3818f0ee342211ae5b381a31f94d32 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jan 2026 15:24:30 +0100 Subject: [PATCH 13/15] Images, Style: added style.ImageRounding, ImGuiStyleVar_ImageRounding to configure rounding of Image() widgets. (#2942, #845) Moving border drawing above AddImage() call, should not make a difference for square images. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 3 +++ imgui.h | 2 ++ imgui_demo.cpp | 1 + imgui_widgets.cpp | 12 ++++++++---- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0645eff90..1a732a04d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -167,6 +167,9 @@ Other Changes: size. (#9166, #7623) - Added GetItemFlags() in public API for consistency and to expose generic flags of last submitted item. (#9127) +- Images: + - Added style.ImageRounding, ImGuiStyleVar_ImageRounding to configure + rounding of Image() widgets. (#2942, #845) - Shortcuts: - IsItemHovered() without ImGuiHoveredFlags_AllowWhenBlockedByActiveItem doesn't filter out the signal when activated item is a shortcut remote activation; diff --git a/imgui.cpp b/imgui.cpp index 2a63f26e7..105041d18 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1470,6 +1470,7 @@ ImGuiStyle::ImGuiStyle() GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. + ImageRounding = 0.0f; // Rounding of Image() calls. ImageBorderSize = 0.0f; // Thickness of border around tabs. TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. @@ -1544,6 +1545,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) GrabMinSize = ImTrunc(GrabMinSize * scale_factor); GrabRounding = ImTrunc(GrabRounding * scale_factor); LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); + ImageRounding = ImTrunc(ImageRounding * scale_factor); ImageBorderSize = ImTrunc(ImageBorderSize * scale_factor); TabRounding = ImTrunc(TabRounding * scale_factor); TabMinWidthBase = ImTrunc(TabMinWidthBase * scale_factor); @@ -3603,6 +3605,7 @@ static const ImGuiStyleVarInfo GStyleVarsInfo[] = { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) }, // ImGuiStyleVar_ScrollbarPadding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageRounding) }, // ImGuiStyleVar_ImageRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) }, // ImGuiStyleVar_ImageBorderSize { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) }, // ImGuiStyleVar_TabBorderSize diff --git a/imgui.h b/imgui.h index fb2a505af..33c642b00 100644 --- a/imgui.h +++ b/imgui.h @@ -1830,6 +1830,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_ScrollbarPadding, // float ScrollbarPadding ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding + ImGuiStyleVar_ImageRounding, // float ImageRounding ImGuiStyleVar_ImageBorderSize, // float ImageBorderSize ImGuiStyleVar_TabRounding, // float TabRounding ImGuiStyleVar_TabBorderSize, // float TabBorderSize @@ -2300,6 +2301,7 @@ struct ImGuiStyle float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. + float ImageRounding; // Rounding of Image() calls. float ImageBorderSize; // Thickness of border around Image() calls. float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. float TabBorderSize; // Thickness of border around tabs. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index b01c86940..5db91155b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8459,6 +8459,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f"); SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); + SliderFloat("ImageRounding", &style.ImageRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, 1.0f, "%.0f"); SeparatorText("Tooltips"); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3b6ee2156..1071d3d8c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1130,11 +1130,15 @@ void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const Im return; // Render - if (g.Style.ImageBorderSize > 0.0f) - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize); + float rounding = g.Style.ImageRounding; if (bg_col.w > 0.0f) - window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); - window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col), rounding); + if (rounding > 0.0f) + window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), rounding); + else + window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + if (g.Style.ImageBorderSize > 0.0f) + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), rounding, ImDrawFlags_None, g.Style.ImageBorderSize); } void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1) From a1dfaf4869347b8f639f63cfc2636f2d9d8b20db Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jan 2026 15:54:55 +0100 Subject: [PATCH 14/15] ImageButton() doesn't use a clamped style.FrameRounding value but instead adjust inner image rounding when FramePadding > FrameRounding. (#2942, #845) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1a732a04d..2cc2d9152 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -170,6 +170,8 @@ Other Changes: - Images: - Added style.ImageRounding, ImGuiStyleVar_ImageRounding to configure rounding of Image() widgets. (#2942, #845) + - ImageButton() doesn't use a clamped style.FrameRounding value but instead + adjust inner image rounding when FramePadding > FrameRounding. (#2942, #845) - Shortcuts: - IsItemHovered() without ImGuiHoveredFlags_AllowWhenBlockedByActiveItem doesn't filter out the signal when activated item is a shortcut remote activation; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1071d3d8c..0a044a97b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1178,10 +1178,14 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_ // Render const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); RenderNavCursor(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding)); + RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col)); - window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); + float image_rounding = ImMax(g.Style.FrameRounding - ImMax(padding.x, padding.y), g.Style.ImageRounding); + if (image_rounding > 0.0f) + window->DrawList->AddImageRounded(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col), image_rounding); + else + window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); return pressed; } From 9a6eb0ab2564925aac1bd1d9a7a3b3619192ca57 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jan 2026 19:23:57 +0100 Subject: [PATCH 15/15] Backends: Vulkan: ImGui_ImplVulkanH_DestroyWindow() oes not call vkDestroySurfaceKHR(): because surface is created by caller. (#9163) # Conflicts: # backends/imgui_impl_vulkan.cpp --- backends/imgui_impl_vulkan.cpp | 32 +++++++++++++++----------- backends/imgui_impl_vulkan.h | 5 ++-- docs/CHANGELOG.txt | 4 ++++ examples/example_glfw_vulkan/main.cpp | 12 +++++----- examples/example_sdl2_vulkan/main.cpp | 12 +++++----- examples/example_sdl3_vulkan/main.cpp | 12 +++++----- examples/example_win32_vulkan/main.cpp | 12 +++++----- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index f0ca7907a..f01d2145d 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -27,12 +27,13 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2026-01-05: Vulkan: Helper for creating render pass uses ImGui_ImplVulkanH_Window::AttachmentDesc to create render pass. Removed ClearEnabled. (#9152) -// 2025-11-24: Vulkan: Helper for creating a swap-chain (used by examples and multi-viewports) selects VkSwapchainCreateInfoKHR's compositeAlpha based on cap.supportedCompositeAlpha. (#8784) +// 2025-09-26: [Helpers] *BREAKING CHANGE*: Vulkan: Helper ImGui_ImplVulkanH_DestroyWindow() does not call vkDestroySurfaceKHR(): as surface is created by caller of ImGui_ImplVulkanH_CreateOrResizeWindow(), it is more consistent that we don't destroy it. (#9163) +// 2026-01-05: [Helpers] *BREAKING CHANGE*: Vulkan: Helper for creating render pass uses ImGui_ImplVulkanH_Window::AttachmentDesc to create render pass. Removed ClearEnabled. (#9152) +// 2025-11-24: [Helpers] Vulkan: Helper for creating a swap-chain (used by examples and multi-viewports) selects VkSwapchainCreateInfoKHR's compositeAlpha based on cap.supportedCompositeAlpha. (#8784) // 2025-10-15: Vulkan: Added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to volk.h header. (#9008) // 2025-09-26: *BREAKING CHANGE*: 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. // 2025-09-26: *BREAKING CHANGE*: renamed ImGui_ImplVulkan_MainPipelineCreateInfo to ImGui_ImplVulkan_PipelineInfo. Introduced very recently so shouldn't affect many users. -// 2025-09-26: *BREAKING CHANGE*: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a VkImageUsageFlags image_usage` argument, default to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT if 0. +// 2025-09-26: [Helpers] *BREAKING CHANGE*: Helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a VkImageUsageFlags image_usage` argument, default to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT if 0. // 2025-09-26: Vulkan: Added a way to customize shaders by filling ImGui_ImplVulkan_InitInfo::CustomShaderVertCreateInfo/CustomShaderFragCreateInfo. (#8585) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-09-04: Vulkan: Added ImGui_ImplVulkan_CreateMainPipeline(). (#8110, #8111) @@ -46,8 +47,8 @@ // 2025-02-14: *BREAKING CHANGE*: Added uint32_t api_version to ImGui_ImplVulkan_LoadFunctions(). // 2025-02-13: Vulkan: Added ApiVersion field in ImGui_ImplVulkan_InitInfo. Default to header version if unspecified. Dynamic rendering path loads "vkCmdBeginRendering/vkCmdEndRendering" (without -KHR suffix) on API 1.3. (#8326) // 2025-01-09: Vulkan: Added IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE to clarify how many image sampler descriptors are expected to be available in descriptor pool. (#6642) -// 2025-01-06: Vulkan: Added more ImGui_ImplVulkanH_XXXX helper functions to simplify our examples. -// 2024-12-11: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) +// 2025-01-06: [Helpers] Vulkan: Added more ImGui_ImplVulkanH_XXXX helper functions to simplify our examples. +// 2024-12-11: [Helpers] Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) // 2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867) // 2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. @@ -55,7 +56,7 @@ // 2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().) // 2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering. // 2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure. -// 2024-01-19: Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) +// 2024-01-19: [Helpers] Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236) // 2024-01-11: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size (#3957). Fixed MinAllocationSize handing (#7189). // 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238) // 2024-01-03: Vulkan: Stopped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them. @@ -83,16 +84,16 @@ // 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. // 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. // 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount(). -// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. +// 2019-04-04: [Helpers] Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. // 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like. // 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int). // 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. +// 2018-08-25: [Helpers] Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. // 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. // 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. // 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. +// 2018-03-03: [Helpers] Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. // 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. @@ -1767,7 +1768,9 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count, VkImageUsageFlags image_usage) { IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); - (void)instance; + IM_ASSERT(wd->Surface != VK_NULL_HANDLE); + IM_UNUSED(instance); + ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count, image_usage); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); @@ -1850,6 +1853,7 @@ void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevic void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) { + IM_UNUSED(instance); vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) //vkQueueWaitIdle(bd->Queue); @@ -1862,9 +1866,11 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui vkDestroyPipeline(device, wd->Pipeline, allocator); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); - vkDestroySurfaceKHR(instance, wd->Surface, allocator); - - *wd = ImGui_ImplVulkanH_Window(); + wd->RenderPass = VK_NULL_HANDLE; + wd->Swapchain = VK_NULL_HANDLE; + wd->Width = wd->Height = 0; + wd->FrameIndex = wd->ImageCount = wd->SemaphoreCount = wd->SemaphoreIndex = 0; + //vkDestroySurfaceKHR(instance, wd->Surface, allocator); // v1.92.6 (~2026-01-16): because wd->Surface is user provided we don't attempt to destroy it ourself. } void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator) diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 83376e597..0ebe1d42d 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -171,6 +171,7 @@ struct ImGui_ImplVulkan_RenderState // Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app. // // You probably do NOT need to use or care about those functions. +// WE DO NOT PROVIDE STRONG GUARANTEES OF BACKWARD/FORWARD COMPATIBILITY. // Those functions only exist because: // 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. // 2) the multi-viewport / platform window implementation needs them internally. @@ -181,8 +182,6 @@ struct ImGui_ImplVulkan_RenderState // render pass, frame buffers, etc.). You may read this code if you are curious, but // it is recommended you use your own custom tailored code to do equivalent work. // -// We don't provide a strong guarantee that we won't change those functions API. -// // The ImGui_ImplVulkanH_XXX functions should NOT interact with any of the state used // by the regular ImGui_ImplVulkan_XXX functions. //------------------------------------------------------------------------- @@ -224,6 +223,7 @@ struct ImGui_ImplVulkanH_Window { // Input bool UseDynamicRendering; + VkSurfaceKHR Surface; // Surface created and destroyed by caller. VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkAttachmentDescription AttachmentDesc; // RenderPass creation: main attachment description. @@ -233,7 +233,6 @@ struct ImGui_ImplVulkanH_Window int Width; // Generally same as passed to ImGui_ImplVulkanH_CreateOrResizeWindow() int Height; VkSwapchainKHR Swapchain; - VkSurfaceKHR Surface; VkRenderPass RenderPass; VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2cc2d9152..6de32507b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -105,6 +105,10 @@ Breaking Changes: - BeginPopupContextItem("foo", 1); // Behavior unchanged (as a courtesy we legacy interpret 1 as ImGuiPopupFlags_MouseButtonRight, will assert if disabling legacy behaviors. - BeginPopupContextItem("foo", 0); // !! Behavior changed !! Was Left button. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft. - BeginPopupContextItem("foo", ImGuiPopupFlags_NoReopen); // !! Behavior changed !! Was Left button + flags. Now will defaults to Right button! --> Use ImGuiPopupFlags_MouseButtonLeft | xxx. +- Backends: + - Vulkan: optional ImGui_ImplVulkanH_DestroyWindow() helper used by our example + code does not call vkDestroySurfaceKHR(): because surface is created by caller + of ImGui_ImplVulkanH_CreateOrResizeWindow(), it is more consistent. (#9163) Other Changes: diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index ab259a3e5..ccce94fb5 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -212,11 +212,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -226,6 +224,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->Surface = surface; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode @@ -256,9 +255,10 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) @@ -532,7 +532,7 @@ int main(int, char**) ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); glfwDestroyWindow(window); diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index 58f4f8e10..87a38ff90 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -203,11 +203,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -217,6 +215,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->Surface = surface; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode @@ -247,9 +246,10 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) @@ -547,7 +547,7 @@ int main(int, char**) ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); SDL_DestroyWindow(window); diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp index fa2296938..38c48d3a7 100644 --- a/examples/example_sdl3_vulkan/main.cpp +++ b/examples/example_sdl3_vulkan/main.cpp @@ -205,11 +205,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -219,6 +217,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->Surface = surface; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode @@ -249,9 +248,10 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) @@ -550,7 +550,7 @@ int main(int, char**) ImGui_ImplSDL3_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); SDL_DestroyWindow(window); diff --git a/examples/example_win32_vulkan/main.cpp b/examples/example_win32_vulkan/main.cpp index b70cb9fcc..75c5605cf 100644 --- a/examples/example_win32_vulkan/main.cpp +++ b/examples/example_win32_vulkan/main.cpp @@ -201,11 +201,9 @@ static void SetupVulkan(ImVector instance_extensions) // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { - wd->Surface = surface; - // Check for WSI support VkBool32 res; - vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res); + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); @@ -215,6 +213,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->Surface = surface; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode @@ -245,9 +244,10 @@ static void CleanupVulkan() vkDestroyInstance(g_Instance, g_Allocator); } -static void CleanupVulkanWindow() +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { - ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator); + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) @@ -516,7 +516,7 @@ int main(int, char**) ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); - CleanupVulkanWindow(); + CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); ::DestroyWindow(hwnd);