From 03a9946a1440b0061c8096d075d1df96ca77eb1c Mon Sep 17 00:00:00 2001 From: xttt Date: Wed, 11 Mar 2026 20:01:49 +0100 Subject: [PATCH 01/12] InputText: reworked so that io.ConfigInputTextEnterKeepActive reactivate in order for e.g. IsItemDeactivatedAfterEdit() to work. (#9001, #9115) --- imgui.cpp | 1 + imgui_internal.h | 1 + imgui_widgets.cpp | 12 +++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index f3b0c7b5..b88ff1d8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4281,6 +4281,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) MouseStationaryTimer = 0.0f; InputTextPasswordFontBackupFlags = ImFontFlags_None; + InputTextReactivateID = 0; TempInputId = 0; memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); BeginMenuDepth = BeginComboDepth = 0; diff --git a/imgui_internal.h b/imgui_internal.h index 0de97ac2..3cfac423 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2461,6 +2461,7 @@ struct ImGuiContext ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFontBaked InputTextPasswordFontBackupBaked; ImFontFlags InputTextPasswordFontBackupFlags; + ImGuiID InputTextReactivateID; // ID of InputText to reactivate on next frame (for ConfigInputTextEnterKeepActive behavior) ImGuiID TempInputId; // Temporary text input when using Ctrl+Click on a slider, etc. ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types int BeginMenuDepth; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cbb51263..355b63a7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4762,6 +4762,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard))); + // Check if this InputText should be reactivated (for ConfigInputTextEnterKeepActive) + const bool input_requested_by_reactivate = (g.InputTextReactivateID == id); + if (input_requested_by_reactivate) + g.InputTextReactivateID = 0; // Clear the flag + const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); @@ -4772,7 +4777,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf); const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. - const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav); + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_reactivate); const bool init_state = (init_make_active || user_scroll_active); if (init_reload_from_user_buf) { @@ -5111,7 +5116,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { validated = true; if (io.ConfigInputTextEnterKeepActive && !is_multiline) + { + // Deactivate for one frame to trigger IsItemDeactivatedAfterEdit(), then reactivate state->SelectAll(); // No need to scroll + clear_active_id = true; + g.InputTextReactivateID = id; // Mark for reactivation on next frame + } else clear_active_id = true; } From 5aa7d61139b8857f413d72ada13618e0a9e9dc55 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Mar 2026 20:33:18 +0100 Subject: [PATCH 02/12] InputText: reworked so that io.ConfigInputTextEnterKeepActive reactivate in order for e.g. IsItemDeactivatedAfterEdit() to work. Amends. (#9001, #9115) Rework. Fixes dangling InputTextReactivateId in case of field being hidden on activation. --- imgui.cpp | 4 +++- imgui.h | 2 +- imgui_demo.cpp | 2 +- imgui_internal.h | 2 +- imgui_widgets.cpp | 16 ++++------------ 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b88ff1d8..3638f6bf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4281,7 +4281,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) MouseStationaryTimer = 0.0f; InputTextPasswordFontBackupFlags = ImFontFlags_None; - InputTextReactivateID = 0; + InputTextReactivateId = 0; TempInputId = 0; memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); BeginMenuDepth = BeginComboDepth = 0; @@ -5573,6 +5573,8 @@ void ImGui::NewFrame() g.ActiveIdIsJustActivated = false; if (g.TempInputId != 0 && g.ActiveId != g.TempInputId) g.TempInputId = 0; + if (g.InputTextReactivateId != 0 && g.InputTextReactivateId != g.DeactivatedItemData.ID) + g.InputTextReactivateId = 0; if (g.ActiveId == 0) { g.ActiveIdUsingNavDirMask = 0x00; diff --git a/imgui.h b/imgui.h index eca23ac1..cc82c5f3 100644 --- a/imgui.h +++ b/imgui.h @@ -2428,7 +2428,7 @@ struct ImGuiIO bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). - bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will keep item active and select contents (single-line only). + bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will reactivate item and select all text (single-line only). bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index da8a78f8..d7dc0b02 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -526,7 +526,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting)."); ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive); - ImGui::SameLine(); HelpMarker("Pressing Enter will keep item active and select contents (single-line only)."); + ImGui::SameLine(); HelpMarker("Pressing Enter will reactivate item and select all text (single-line only)."); ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText); ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving)."); ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors); diff --git a/imgui_internal.h b/imgui_internal.h index 3cfac423..c5404074 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2461,7 +2461,7 @@ struct ImGuiContext ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFontBaked InputTextPasswordFontBackupBaked; ImFontFlags InputTextPasswordFontBackupFlags; - ImGuiID InputTextReactivateID; // ID of InputText to reactivate on next frame (for ConfigInputTextEnterKeepActive behavior) + ImGuiID InputTextReactivateId; // ID of InputText to reactivate on next frame (for io.ConfigInputTextEnterKeepActive behavior) ImGuiID TempInputId; // Temporary text input when using Ctrl+Click on a slider, etc. ImGuiDataTypeStorage DataTypeZeroValue; // 0 for all data types int BeginMenuDepth; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 355b63a7..78fae523 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4761,12 +4761,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize)); const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard))); - - // Check if this InputText should be reactivated (for ConfigInputTextEnterKeepActive) - const bool input_requested_by_reactivate = (g.InputTextReactivateID == id); - if (input_requested_by_reactivate) - g.InputTextReactivateID = 0; // Clear the flag - + const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive const bool user_clicked = hovered && io.MouseClicked[0]; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y); @@ -5114,16 +5109,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ bool is_new_line = is_multiline && !is_gamepad_validate && (is_shift_enter || (is_enter && !ctrl_enter_for_new_line) || (is_ctrl_enter && ctrl_enter_for_new_line)); if (!is_new_line) { - validated = true; + validated = clear_active_id = true; if (io.ConfigInputTextEnterKeepActive && !is_multiline) { - // Deactivate for one frame to trigger IsItemDeactivatedAfterEdit(), then reactivate + // Queue reactivation, so that e.g. IsItemDeactivatedAfterEdit() will work. (#9001) state->SelectAll(); // No need to scroll - clear_active_id = true; - g.InputTextReactivateID = id; // Mark for reactivation on next frame + g.InputTextReactivateId = id; // Mark for reactivation on next frame } - else - clear_active_id = true; } else if (!is_readonly) { From 378cb85bf0810150b830b24b3f650fdfda5e7e1b Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Mar 2026 21:02:46 +0100 Subject: [PATCH 03/12] Amend missing Changelog entry. (#9115) --- docs/CHANGELOG.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ff03643c..176b7e6b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -72,6 +72,9 @@ Other Changes: - InputText: - Shift+Enter in multi-line editor always adds a new line, regardless of ImGuiInputTextFlags_CtrlEnterForNewLine being set or not. (#9239) + - Reworked io.ConfigInputTextEnterKeepActive mode so that pressing Enter will + deactivate/reactivate the item in order for e.g. IsItemDeactivatedAfterEdit() + signals to be emitted the same way regardless of that setting. (#9001, #9115) - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness From 5220c14f4b880dfe6b61ee105dedcc59d02c0d29 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 11 Mar 2026 21:16:49 +0100 Subject: [PATCH 04/12] Docs: update readme. --- docs/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/README.md b/docs/README.md index a409b930..283dd479 100644 --- a/docs/README.md +++ b/docs/README.md @@ -55,8 +55,8 @@ if (ImGui::Button("Save")) ImGui::InputText("string", buf, IM_COUNTOF(buf)); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); ``` -sample code output (dark) -sample code output (light) +sample code output (dark) +sample code output (light) ```cpp // Create a window called "My First Tool", with a menu bar. @@ -150,10 +150,10 @@ Officially maintained backends (in repository): - Frameworks: AGS/Adventure Game Studio, Amethyst, Blender, bsf, Cinder, Cocos2d-x, Defold, Diligent Engine, Ebiten, Flexium, GML/Game Maker Studio, GLEQ, Godot, GTK3, Irrlicht Engine, JUCE, LÖVE+LUA, Mach Engine, Magnum, Marmalade, Monogame, NanoRT, nCine, Nim Game Lib, Nintendo 3DS/Switch/WiiU (homebrew), Ogre, openFrameworks, OSG/OpenSceneGraph, Orx, Photoshop, px_render, Qt/QtDirect3D, raylib, SFML, Sokol, Unity, Unreal Engine 4/5, UWP, vtk, VulkanHpp, VulkanSceneGraph, Win32 GDI, WxWidgets. - Many bindings are auto-generated (by good old [cimgui](https://github.com/cimgui/cimgui) or our newer [dear_bindings](https://github.com/dearimgui/dear_bindings)), you can use their metadata output to generate bindings for other languages. -Useful extensions - [Useful Extensions/Widgets](https://github.com/ocornut/imgui/wiki/Useful-Extensions) wiki page: -- Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). + +[![Useful extensions thumbnails](https://github.com/user-attachments/assets/e6b0aa7c-bf53-41c5-ac69-bea3098b1dee)](https://github.com/ocornut/imgui/wiki/Useful-Extensions) +- Automation/testing, Text editors, node editors, timeline editors, plotting, software renderers, remote network access, memory editors, gizmos, etc. Notable and well supported extensions include [ImPlot](https://github.com/epezent/implot), [ImPlot3d](https://github.com/brenocq/implot3d) and [Dear ImGui Test Engine](https://github.com/ocornut/imgui_test_engine). Also see [Wiki](https://github.com/ocornut/imgui/wiki) for more links and ideas. From 7546f1eb1688c1e3bc40ca1c1c4376a49510bf9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=B6hme?= Date: Wed, 11 Jun 2025 17:06:31 +0200 Subject: [PATCH 05/12] Backends: Vulkan: ImGui_ImplVulkan_AddTexture() skips updating descriptor_set if failing to allocate. (#8677) Reduce error surface to the check_vk_result() call. --- backends/imgui_impl_vulkan.cpp | 1 + docs/CHANGELOG.txt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 7bfd48a3..1b4e81c2 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1376,6 +1376,7 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image } // Update the Descriptor Set: + if (descriptor_set != VK_NULL_HANDLE) { VkDescriptorImageInfo desc_image[1] = {}; desc_image[0].sampler = sampler; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 176b7e6b..43a7f788 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -107,6 +107,8 @@ Other Changes: - hidden scrollbar in Firefox. - Vulkan: added ImGui_ImplVulkan_PipelineInfo::ExtraDynamicStates[] to allow specifying extra dynamic states to add when creating the VkPipeline. (#9211) [@DziubanMaciej] + - Vulkan: ImGui_ImplVulkan_AddTexture() skips updating descriptor_set if failing + to allocate one. (#8677) [@micb25] - WebGPU: fixed undefined behaviors in example code for requesting adapter and device. (#9246, #9256) [@r-lyeh] - GLFW/SDL2/SDL3+WebGPU: removed suport for Emscripten <4.0.10. (#9281) [@ypujante] From 1fbab15c0ab06392299d3d0374082010741174e5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 11 Mar 2026 21:59:44 +0100 Subject: [PATCH 06/12] Focus: fixed fallback "Debug" window temporarily taking focus and setting io.WantCaptureKeyboard for a frame. (#9243) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 43a7f788..d812e8b9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -92,6 +92,8 @@ Other Changes: - Implemented a custom tweak to extend hit-testing bounding box when window is sitting at the edge of a viewport (e.g. fullscreen or docked window), so that e.g. mouse the mouse at the extreme of the screen will reach the scrollbar. (#9276) +- Focus: fixed fallback "Debug" window temporarily taking focus and setting io.WantCaptureKeyboard + for one frame on e.g. application boot if no other windows are submitted. (#9243) - Demo: fixed IMGUI_DEMO_MARKER locations for examples applets. (#9261, #3689) [@pthom] - Backends: - SDLGPU3: removed unnecessary call to SDL_WaitForGPUIdle when releasing diff --git a/imgui.cpp b/imgui.cpp index 3638f6bf..d716a5a3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5985,10 +5985,14 @@ void ImGui::EndFrame() } g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0; - // Hide implicit/fallback "Debug" window if it hasn't been used + // Hide and unfocus implicit/fallback "Debug" window if it hasn't been used g.WithinFrameScopeWithImplicitWindow = false; - if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) + if (g.CurrentWindow && g.CurrentWindow->IsFallbackWindow && g.CurrentWindow->WriteAccessed == false) + { g.CurrentWindow->Active = false; + if (g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow) + FocusWindow(NULL); + } End(); // Update navigation: Ctrl+Tab, wrap-around requests From 0db591935f08c73f1e0726869a92ca803e8660a9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Mar 2026 14:23:29 +0100 Subject: [PATCH 07/12] Changed default ImTextureID_Invalid value to -1 instead of 0 +added comments. (#9293, #8745, #8465, #7090) --- docs/CHANGELOG.txt | 6 ++++++ imgui.cpp | 6 ++++-- imgui.h | 14 +++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d812e8b9..a88d8d0a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,12 @@ HOW TO UPDATE? Breaking Changes: + - Changed default ImTextureID_Invalid value to -1 instead of 0 if not #define-d. + (#9293, #8745, #8465, #7090) + - It seems like a better default since it will work with backends storing + indices or memory offsets inside ImTextureID, where 0 might be a valid value. + - If this is causing problem with e.g your custom ImTextureID definition, you can + add '#define ImTextureID_Invalid 0' to your imconfig.h + PLEASE report this to GitHub. - Separator(): fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator. The fix could affect code e.g. computing height from multiple widgets in order to diff --git a/imgui.cpp b/imgui.cpp index d716a5a3..291f0fce 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -394,6 +394,9 @@ 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/03/12 (1.92.7) - Changed default ImTextureID_Invalid to -1 instead of 0 if not #define-d. (#9293, #8745, #8465, #7090) + It seems like a better default since it will work with backends storing indices or memory offsets inside ImTextureID, where 0 might be a valid value. + If this is causing problem with e.g. your custom ImTextureID definition, you can add '#define ImTextureID_Invalid 0' to your imconfig.h + PLEASE report this to GitHub. - 2026/02/26 (1.92.7) - Separator: fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator. The fix could affect code e.g. computing height from multiple widgets in order to allocate vertical space for a footer or multi-line status bar. (#2657, #9263) The "Console" example had such a bug: @@ -402,8 +405,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: Should be: float footer_height = style.ItemSpacing.y + style.SeparatorSize + ImGui::GetFrameHeightWithSpacing(); BeginChild("ScrollingRegion", { 0, -footer_height }); - When such idiom was used and assuming zero-height Separator, it is likely that - in 1.92.7 the resulting window will have unexpected 1 pixel scrolling range. + When such idiom was used and assuming zero-height Separator, it is likely that in 1.92.7 the resulting window will have unexpected 1 pixel scrolling range. - 2026/02/23 (1.92.7) - Commented out legacy signature for Combo(), ListBox(), signatures which were obsoleted in 1.90 (Nov 2023), when the getter callback type was changed. - Old getter type: bool (*getter)(void* user_data, int idx, const char** out_text) // Set label + return bool. False replaced label with placeholder. - New getter type: const char* (*getter)(void* user_data, int idx) // Return label or NULL/empty label if missing diff --git a/imgui.h b/imgui.h index cc82c5f3..a41b91b5 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.7 WIP" -#define IMGUI_VERSION_NUM 19264 +#define IMGUI_VERSION_NUM 19265 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -340,9 +340,11 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that. #endif -// Define this if you need 0 to be a valid ImTextureID for your backend. +// Define this if you need to change the invalid value for your backend. +// - in v1.92.7 (2025/03/12): we changed default value from 0 to -1 as it is a better default, which supports storing offsets/indices. +// - If this is causing problem with your custom ImTextureID definition, you can add '#define ImTextureID_Invalid' to your imconfig + please report this to GitHub. #ifndef ImTextureID_Invalid -#define ImTextureID_Invalid ((ImTextureID)0) +#define ImTextureID_Invalid ((ImTextureID)-1) #endif // ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*. @@ -3906,8 +3908,10 @@ inline ImTextureID ImTextureRef::GetTexID() const // Using an indirection to avoid patching ImDrawCmd after a SetTexID() call (but this could be an alternative solution too) inline ImTextureID ImDrawCmd::GetTexID() const { - // If you are getting this assert: A renderer backend with support for ImGuiBackendFlags_RendererHasTextures (1.92) - // must iterate and handle ImTextureData requests stored in ImDrawData::Textures[]. + // If you are getting this assert with ImTextureID_Invalid == 0 and your ImTextureID is used to store an index: + // - You can add '#define ImTextureID_Invalid ((ImTextureID)-1)' in your imconfig file. + // If you are getting this assert with a renderer backend with support for ImGuiBackendFlags_RendererHasTextures (1.92+): + // - You must correctly iterate and handle ImTextureData requests stored in ImDrawData::Textures[]. See docs/BACKENDS.md. ImTextureID tex_id = TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID; // == TexRef.GetTexID() above. if (TexRef._TexData != NULL) IM_ASSERT(tex_id != ImTextureID_Invalid && "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!"); From a1038261542442730d1ca6bf00a810a0acae92a4 Mon Sep 17 00:00:00 2001 From: exelix <13405476+exelix11@users.noreply.github.com> Date: Tue, 3 Mar 2026 16:41:24 +0100 Subject: [PATCH 08/12] Nav: allow ImGuiKey_Menu or Shift + F10 to open context menus. (#8803, #9270) --- imgui.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 291f0fce..84552982 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12505,7 +12505,11 @@ void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + bool isMouseInvocation = IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + bool isKeyboardInvocation = false; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsItemFocused()) + isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift); + if (isKeyboardInvocation || isMouseInvocation) { ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) @@ -12538,7 +12542,11 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + bool isMouseInvocation = IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + bool isKeyboardInvocation = false; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsItemFocused()) + isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift); + if (isKeyboardInvocation || isMouseInvocation) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } @@ -12551,8 +12559,12 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl str_id = "window_context"; ImGuiID id = window->GetID(str_id); ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); - if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) + bool isMouseInvocation = IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + bool isKeyboardInvocation = false; + if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsWindowFocused()) + isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift); + if (isMouseInvocation || isKeyboardInvocation) + if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered() || isKeyboardInvocation) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } From 6cc99a6e2b32b200052203f2aba5c760e5dee029 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Mar 2026 18:23:50 +0100 Subject: [PATCH 09/12] Nav: allow ImGuiKey_Menu or Shift + F10 to open context menus. Amends. (#8803, #9270) This doesn't attempt to move the shortcut polling in NavUpdate() yet. --- docs/CHANGELOG.txt | 4 +++ imgui.cpp | 74 +++++++++++++++++++++++++++++++--------------- imgui_demo.cpp | 1 + imgui_internal.h | 2 ++ 4 files changed, 57 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a88d8d0a..161a4e6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,10 @@ Other Changes: - TextLink() underline thickness. - ColorButton() border thickness. - Separator() thickness, via scaling newly added style.SeparatorSize. (#2657, #9263) +- Nav: + - Popups: Shift+F10 or Menu key can now open popups menus when using + BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick(). + (#8803, #9270) [@exelix11, @ocornut] - Clipper: - Clear `DisplayStart`/`DisplayEnd` fields when `Step()` returns false. - Added `UserIndex` helper storage. This is solely a convenience for cases where diff --git a/imgui.cpp b/imgui.cpp index 84552982..422cd22b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -167,6 +167,7 @@ CODE - Home, End Scroll to top, scroll to bottom. - Alt Toggle between scrolling layer and menu layer. - Ctrl+Tab then Ctrl+Arrows Move window. Hold Shift to resize instead of moving. + - Menu or Shift+F10 Open context menu. - Output when ImGuiConfigFlags_NavEnableKeyboard set, - io.WantCaptureKeyboard flag is set when keyboard is claimed. - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. @@ -12498,21 +12499,57 @@ ImGuiMouseButton ImGui::GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags) return ImGuiMouseButton_Right; // Default == 1 } +bool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); + if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + return true; + + if (g.NavId == id && g.NavId != 0) // == IsItemFocused() + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) + if (Shortcut(ImGuiKey_F10 | ImGuiMod_Shift) || IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner)) + { + g.NavInputSource = ImGuiInputSource_Keyboard; + SetNavCursorVisibleAfterMove(); + return true; + } + + return false; +} + +bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags, ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); + if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) + return true; + + // FIXME-NAV: How to meaningful support ImGuiPopupFlags_NoOpenOverItems with keyboard? + if (g.CurrentWindow->ID == id && g.NavWindow != NULL) + if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) + if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false)) + if (Shortcut(ImGuiKey_F10 | ImGuiMod_Shift) || IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner)) + { + g.NavInputSource = ImGuiInputSource_Keyboard; + SetNavCursorVisibleAfterMove(); + return true; + } + + return false; +} + // Helper to open a popup if mouse button is released over the item // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); - bool isMouseInvocation = IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - bool isKeyboardInvocation = false; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsItemFocused()) - isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift); - if (isKeyboardInvocation || isMouseInvocation) + if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID)) { - ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) OpenPopupEx(id, popup_flags); } } @@ -12539,14 +12576,9 @@ bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flag ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; - ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); - bool isMouseInvocation = IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - bool isKeyboardInvocation = false; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsItemFocused()) - isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift); - if (isKeyboardInvocation || isMouseInvocation) + ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItem ID. Using LastItem ID as a Popup ID won't conflict! + IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) + if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID)) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } @@ -12558,14 +12590,8 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); - ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); - bool isMouseInvocation = IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - bool isKeyboardInvocation = false; - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && IsWindowFocused()) - isKeyboardInvocation = IsKeyReleased(ImGuiKey_Menu) || (IsKeyReleased(ImGuiKey_F10) && g.IO.KeyShift); - if (isMouseInvocation || isKeyboardInvocation) - if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered() || isKeyboardInvocation) - OpenPopupEx(id, popup_flags); + if (IsPopupOpenRequestForWindow(popup_flags, window->ID)) + OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d7dc0b02..e8a43924 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8738,6 +8738,7 @@ void ImGui::ShowUserGuide() BulletText("Return to input text into a widget."); BulletText("Escape to deactivate a widget, close popup,\nexit a child window or the menu layer, clear focus."); BulletText("Alt to jump to the menu layer of a window."); + BulletText("Menu or Shift+F10 to open a context menu."); Unindent(); } diff --git a/imgui_internal.h b/imgui_internal.h index c5404074..370f3cc5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3331,6 +3331,8 @@ namespace ImGui IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); IMGUI_API ImGuiMouseButton GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags); + IMGUI_API bool IsPopupOpenRequestForItem(ImGuiPopupFlags flags, ImGuiID id); + IMGUI_API bool IsPopupOpenRequestForWindow(ImGuiPopupFlags flags, ImGuiID id); // Tooltips IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); From 14a500a4760aaa74bc5913bb9de6d13035e5bd0b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Mar 2026 18:49:57 +0100 Subject: [PATCH 10/12] Nav: allow ImGuiKey_Menu or Shift + F10 to open context menus. Rework with polling in NavUpdate(). (#8803, #9270) This might be a little less flexible but removes burden from the higher-frequency calls. --- imgui.cpp | 51 +++++++++++++++++++++++++----------------------- imgui_internal.h | 4 +++- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 422cd22b..5e4c1606 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1358,6 +1358,7 @@ static void NavUpdateWindowing(); static void NavUpdateWindowingApplyFocus(ImGuiWindow* window); static void NavUpdateWindowingOverlay(); static void NavUpdateCancelRequest(); +static void NavUpdateContextMenuRequest(); static void NavUpdateCreateMoveRequest(); static void NavUpdateCreateTabbingRequest(); static float NavUpdatePageUpPageDown(); @@ -4215,6 +4216,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) NavWindow = NULL; NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; NavLayer = ImGuiNavLayer_Main; + NavOpenContextMenuItemId = NavOpenContextMenuWindowId = 0; NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; NavHighlightActivatedId = 0; @@ -12505,38 +12507,21 @@ bool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id) ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) return true; - - if (g.NavId == id && g.NavId != 0) // == IsItemFocused() - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - if (Shortcut(ImGuiKey_F10 | ImGuiMod_Shift) || IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner)) - { - g.NavInputSource = ImGuiInputSource_Keyboard; - SetNavCursorVisibleAfterMove(); - return true; - } - + if (g.NavOpenContextMenuItemId == id && IsItemFocused()) + return true; return false; } -bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags, ImGuiID id) +bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered()) return true; - - // FIXME-NAV: How to meaningful support ImGuiPopupFlags_NoOpenOverItems with keyboard? - if (g.CurrentWindow->ID == id && g.NavWindow != NULL) - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false)) - if (Shortcut(ImGuiKey_F10 | ImGuiMod_Shift) || IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner)) - { - g.NavInputSource = ImGuiInputSource_Keyboard; - SetNavCursorVisibleAfterMove(); - return true; - } - + if (g.NavOpenContextMenuWindowId && g.CurrentWindow->ID) + if (IsWindowChildOf(g.NavWindow, g.CurrentWindow, false)) // This enable ordering to be used to disambiguate item vs window (#8803) + return true; return false; } @@ -12590,7 +12575,7 @@ bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_fl if (!str_id) str_id = "window_context"; ImGuiID id = window->GetID(str_id); - if (IsPopupOpenRequestForWindow(popup_flags, window->ID)) + if (IsPopupOpenRequestForWindow(popup_flags)) OpenPopupEx(id, popup_flags); return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings); } @@ -13768,6 +13753,7 @@ static void ImGui::NavUpdate() // Process NavCancel input (to close a popup, get back to parent, clear focus) NavUpdateCancelRequest(); + NavUpdateContextMenuRequest(); // Process manual activation request g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0; @@ -14252,6 +14238,23 @@ static void ImGui::NavUpdateCancelRequest() } } +static void ImGui::NavUpdateContextMenuRequest() +{ + ImGuiContext& g = *GImGui; + g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0; + const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if (!nav_keyboard_active || g.NavWindow == NULL) + return; + + const bool request = IsKeyReleased(ImGuiKey_Menu, ImGuiKeyOwner_NoOwner) || (IsKeyPressed(ImGuiKey_F10, ImGuiInputFlags_None, ImGuiKeyOwner_NoOwner) && g.IO.KeyMods == ImGuiMod_Shift); + if (!request) + return; + g.NavOpenContextMenuItemId = g.NavId; + g.NavOpenContextMenuWindowId = g.NavWindow->ID; + g.NavInputSource = ImGuiInputSource_Keyboard; + SetNavCursorVisibleAfterMove(); +} + // Handle PageUp/PageDown/Home/End keys // Called from NavUpdateCreateMoveRequest() which will use our output to create a move request // FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference diff --git a/imgui_internal.h b/imgui_internal.h index 370f3cc5..cd1ae966 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2332,6 +2332,8 @@ struct ImGuiContext ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavHighlightActivatedId; float NavHighlightActivatedTimer; + ImGuiID NavOpenContextMenuItemId; + ImGuiID NavOpenContextMenuWindowId; 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_Gamepad @@ -3332,7 +3334,7 @@ namespace ImGui IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); IMGUI_API ImGuiMouseButton GetMouseButtonFromPopupFlags(ImGuiPopupFlags flags); IMGUI_API bool IsPopupOpenRequestForItem(ImGuiPopupFlags flags, ImGuiID id); - IMGUI_API bool IsPopupOpenRequestForWindow(ImGuiPopupFlags flags, ImGuiID id); + IMGUI_API bool IsPopupOpenRequestForWindow(ImGuiPopupFlags flags); // Tooltips IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); From 90743d3112a703309b9e256ee1d3a3ec90c14b2d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Mar 2026 19:12:47 +0100 Subject: [PATCH 11/12] Nav: allow ImGuiKey_Menu or Shift + F10 to work on Begin()...BeginPopupContextItem() sequence aiming at title bar. (#8803, #9270) --- imgui.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 5e4c1606..fb7e09a5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12507,7 +12507,7 @@ bool ImGui::IsPopupOpenRequestForItem(ImGuiPopupFlags popup_flags, ImGuiID id) ImGuiMouseButton mouse_button = GetMouseButtonFromPopupFlags(popup_flags); if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) return true; - if (g.NavOpenContextMenuItemId == id && IsItemFocused()) + if (g.NavOpenContextMenuItemId == id && (IsItemFocused() || id == g.CurrentWindow->MoveId)) return true; return false; } @@ -14251,6 +14251,11 @@ static void ImGui::NavUpdateContextMenuRequest() return; g.NavOpenContextMenuItemId = g.NavId; g.NavOpenContextMenuWindowId = g.NavWindow->ID; + + // Allow triggering for Begin()..BeginPopupContextItem(). A possible alternative would be to use g.NavLayer == ImGuiNavLayer_Menu. + if (g.NavId == g.NavWindow->GetID("#CLOSE") || g.NavId == g.NavWindow->GetID("#COLLAPSE")) + g.NavOpenContextMenuItemId = g.NavWindow->MoveId; + g.NavInputSource = ImGuiInputSource_Keyboard; SetNavCursorVisibleAfterMove(); } From 6dbda97fee1ed0ae0bce5edd7e9b2e1ef61089eb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Mar 2026 19:27:50 +0100 Subject: [PATCH 12/12] Backends: OpenGL2, OpenGL3, SDLRenderer3: replaced erroneous IM_ASSERT(tex->TexID == 0) calls. (#9295, #9293) Amend/fix 0db5919 which revealed this. --- backends/imgui_impl_opengl2.cpp | 2 +- backends/imgui_impl_opengl3.cpp | 2 +- backends/imgui_impl_sdlrenderer3.cpp | 2 +- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index af5618fb..7b860098 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -274,7 +274,7 @@ void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex) { // Create and upload new texture to graphics system //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); - IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); const void* pixels = tex->GetPixels(); GLuint gl_texture_id = 0; diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 6348b01b..893e8b85 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -744,7 +744,7 @@ void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) { // Create and upload new texture to graphics system //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); - IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); const void* pixels = tex->GetPixels(); GLuint gl_texture_id = 0; diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 38a4383c..72bc174e 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -254,7 +254,7 @@ void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) { // Create and upload new texture to graphics system //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); - IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); // Create texture diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 161a4e6e..50734a02 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,10 @@ Breaking Changes: indices or memory offsets inside ImTextureID, where 0 might be a valid value. - If this is causing problem with e.g your custom ImTextureID definition, you can add '#define ImTextureID_Invalid 0' to your imconfig.h + PLEASE report this to GitHub. + - If you have hardcoded e.g. 'if (tex_id == 0)' checks they should be updated. + e.g. OpenGL2, OpenGL3 and SDLRenderer3 backends incorrectly had 'IM_ASSERT(tex->TexID == 0)' + lines which were replaced with 'IM_ASSERT(tex->TexID == ImTextureID_Invalid)'. + If you have copied or forked backends consider fixing locally. (#9295) - Separator(): fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator. The fix could affect code e.g. computing height from multiple widgets in order to diff --git a/imgui.cpp b/imgui.cpp index fb7e09a5..c043989a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -398,6 +398,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - 2026/03/12 (1.92.7) - Changed default ImTextureID_Invalid to -1 instead of 0 if not #define-d. (#9293, #8745, #8465, #7090) It seems like a better default since it will work with backends storing indices or memory offsets inside ImTextureID, where 0 might be a valid value. If this is causing problem with e.g. your custom ImTextureID definition, you can add '#define ImTextureID_Invalid 0' to your imconfig.h + PLEASE report this to GitHub. + If you have hard-coded e.g. 'if (tex_id == 0)' checks they should be updated. e.g. OpenGL2, OpenGL3 and SDLRenderer3 backends incorrectly had 'IM_ASSERT(tex->TexID == 0)' lines which were replaced with 'IM_ASSERT(tex->TexID == ImTextureID_Invalid)'. (#9295) - 2026/02/26 (1.92.7) - Separator: fixed a legacy quirk where Separator() was submitting a zero-height item for layout purpose, even though it draws a 1-pixel separator. The fix could affect code e.g. computing height from multiple widgets in order to allocate vertical space for a footer or multi-line status bar. (#2657, #9263) The "Console" example had such a bug: