From 709be8c49540a6eb64bd068d3961ad095a42c26b Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Mar 2026 15:34:12 +0100 Subject: [PATCH 01/37] Discard/GC of ImDrawList buffers for unused windows favor restoring them to ~Size*1.05 instead of Capacity when awakening again. (#9303) + made "GC now" button process even active windows. --- docs/CHANGELOG.txt | 4 ++++ imgui.cpp | 10 ++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 457518dc..1a4bafa4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -120,6 +120,10 @@ Other Changes: as a convenience for when using e.g. InvisibleButton(). - 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) +- Memory: + - Discard/GC of ImDrawList buffers for unused windows favor restoring them to + ~Size*1.05 instead of Capacity when awakening again. Facilitate releasing ImDrawList + buffers after unusual usage spike. (#9303). - 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 b7c5d1b2..4793e783 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4633,11 +4633,12 @@ void ImGui::GcCompactTransientMiscBuffers() // Not freed: // - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data) // This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost. +// FIXME: Consider exposing of elaborating GC policy, e.g. being able to trim excessive ImDrawList gaps. (#9303) void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window) { window->MemoryCompacted = true; - window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity; - window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity; + window->MemoryDrawListIdxCapacity = ImMin((int)(window->DrawList->IdxBuffer.Size * 1.05f), window->DrawList->IdxBuffer.Capacity); + window->MemoryDrawListVtxCapacity = ImMin((int)(window->DrawList->VtxBuffer.Size * 1.05f), window->DrawList->VtxBuffer.Capacity); window->IDStack.clear(); window->DrawList->_ClearFreeMemory(); window->DC.ChildWindows.clear(); @@ -5665,7 +5666,8 @@ void ImGui::NewFrame() // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); - const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; + const bool gc_all = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f); + const float memory_compact_start_time = gc_all ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (ImGuiWindow* window : g.Windows) { window->WasActive = window->Active; @@ -5675,7 +5677,7 @@ void ImGui::NewFrame() window->BeginCount = 0; // Garbage collect transient buffers of recently unused windows - if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) + if ((!window->WasActive || gc_all) && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) GcCompactTransientWindowBuffers(window); } From 27cacb0e307308743379deb516080eaded566fdb Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Mar 2026 16:48:18 +0100 Subject: [PATCH 02/37] Fixed GetForegroundDrawList()/GetBackgroundDrawList() per-viewport buffers not being collected/ (#9303) --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 18 ++++++++++++++---- imgui_internal.h | 4 ++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1a4bafa4..d8d0c353 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -123,7 +123,9 @@ Other Changes: - Memory: - Discard/GC of ImDrawList buffers for unused windows favor restoring them to ~Size*1.05 instead of Capacity when awakening again. Facilitate releasing ImDrawList - buffers after unusual usage spike. (#9303). + buffers after unusual usage spike. (#9303) + - Fixed GetForegroundDrawList()/GetBackgroundDrawList() per-viewport buffers not being + collected when unused for io.ConfigMemoryCompactTimer amount of time. (#9303) - 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 4793e783..1478018a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5198,12 +5198,12 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw } // Our ImDrawList system requires that there is always a command - if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount) + if (viewport->BgFgDrawListsLastTimeActive[drawlist_no] != (float)g.Time) { draw_list->_ResetForNewFrame(); draw_list->PushTexture(g.IO.Fonts->TexRef); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount; + viewport->BgFgDrawListsLastTimeActive[drawlist_no] = (float)g.Time; } return draw_list; } @@ -6089,7 +6089,7 @@ void ImGui::Render() for (ImGuiViewportP* viewport : g.Viewports) { InitViewportDrawData(viewport); - if (viewport->BgFgDrawLists[0] != NULL) + if (viewport->BgFgDrawLists[0] != NULL && viewport->BgFgDrawListsLastTimeActive[0] == (float)g.Time) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } @@ -6121,7 +6121,7 @@ void ImGui::Render() FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) - if (viewport->BgFgDrawLists[1] != NULL) + if (viewport->BgFgDrawLists[1] != NULL && viewport->BgFgDrawListsLastTimeActive[1] == (float)g.Time) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). @@ -15821,6 +15821,7 @@ static void ImGui::UpdateViewportsNewFrame() main_viewport->FramebufferScale = g.IO.DisplayFramebufferScale; IM_ASSERT(main_viewport->FramebufferScale.x > 0.0f && main_viewport->FramebufferScale.y > 0.0f); + const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; for (ImGuiViewportP* viewport : g.Viewports) { // Lock down space taken by menu bars and status bars @@ -15829,6 +15830,14 @@ static void ImGui::UpdateViewportsNewFrame() viewport->WorkInsetMax = viewport->BuildWorkInsetMax; viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); viewport->UpdateWorkRect(); + + // Garbage collect transient buffers of recently BG/FG drawlists + for (int n = 0; n < IM_COUNTOF(viewport->BgFgDrawLists); n++) + if (viewport->BgFgDrawListsLastTimeActive[n] < memory_compact_start_time && viewport->BgFgDrawLists[n] != NULL) + { + IM_DELETE(viewport->BgFgDrawLists[n]); + viewport->BgFgDrawLists[n] = NULL; + } } } @@ -16867,6 +16876,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiDebugAllocInfo* info = &g.DebugAllocInfo; Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); + Text("Releasing selected unused buffers after: %.2f secs", g.IO.ConfigMemoryCompactTimer); if (SmallButton("GC now")) { g.GcCompactAll = true; } Text("Recent frames with allocations:"); int buf_size = IM_COUNTOF(info->LastEntriesBuf); diff --git a/imgui_internal.h b/imgui_internal.h index c7afcee7..3dbcfd96 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1958,7 +1958,7 @@ struct IMGUI_API ImGuiMultiSelectState // Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { - int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + float BgFgDrawListsLastTimeActive[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData @@ -1972,7 +1972,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset ImVec2 BuildWorkInsetMax; // " - ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } + ImGuiViewportP() { BgFgDrawListsLastTimeActive[0] = BgFgDrawListsLastTimeActive[1] = -1.0f; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) From f4c2f508960d3132e2a41514225107219e2e2d98 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Mar 2026 18:37:04 +0100 Subject: [PATCH 03/37] InputText: fixed a crash when handling ImGuiInputTextFlags_CallbackResize. (#9174) Fix/amend cb3b7ff. --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d8d0c353..8828e28e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -87,6 +87,8 @@ Other Changes: signals to be emitted the same way regardless of that setting. (#9001, #9115) - Fixed a glitch when using ImGuiInputTextFlags_ElideLeft where the local x offset would be incorrect during the deactivation frame. (#9298) + - Fixed a crash introduced in 1.92.6 when handling ImGuiInputTextFlags_CallbackResize + in certain situations. (#9174) - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 22f1bae5..cebe73b8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4497,7 +4497,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* sta callback_data.Flags = flags; callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; callback_data.EventChar = (ImWchar)c; - callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); + callback_data.EventActivated = (state && g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = user_data; if (callback(&callback_data) != 0) return false; @@ -5286,7 +5286,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.ID = id; callback_data.Flags = flags; callback_data.EventFlag = event_flag; - callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); + callback_data.EventActivated = (state && g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = callback_user_data; // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925 @@ -5368,7 +5368,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.ID = id; callback_data.Flags = flags; callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize; - callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); + callback_data.EventActivated = (state != NULL && g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.Buf = buf; callback_data.BufTextLen = apply_new_text_length; callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1); From 6abe65aac686593d6fa25ba4734445f3f8fcd49f Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Mar 2026 18:57:02 +0100 Subject: [PATCH 04/37] InputText: amend fix to avoid PVS-Studio sort of rightful false positive. Amend f4c2f50. (#9174) Checking for state != NULL in the two othr functions where state is already deferenced was misleading. imgui_widgets.cpp:4496:1: error: V595 The 'state' pointer was utilized before it was verified against nullptr. Check lines: 4496, 4500. imgui_widgets.cpp:5273:1: error: V595 The 'state' pointer was utilized before it was verified against nullptr. Check lines: 5273, 5289. --- imgui_widgets.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index cebe73b8..8a2ab9f3 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4402,6 +4402,7 @@ void ImGui::PopPasswordFont() // Return false to discard a character. static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* state, unsigned int* p_char, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) { + IM_ASSERT(state != NULL); unsigned int c = *p_char; ImGuiInputTextFlags flags = state->Flags; @@ -4497,7 +4498,7 @@ static bool InputTextFilterCharacter(ImGuiContext* ctx, ImGuiInputTextState* sta callback_data.Flags = flags; callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; callback_data.EventChar = (ImWchar)c; - callback_data.EventActivated = (state && g.ActiveId == state->ID && g.ActiveIdIsJustActivated); + callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = user_data; if (callback(&callback_data) != 0) return false; @@ -5286,7 +5287,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ callback_data.ID = id; callback_data.Flags = flags; callback_data.EventFlag = event_flag; - callback_data.EventActivated = (state && g.ActiveId == state->ID && g.ActiveIdIsJustActivated); + callback_data.EventActivated = (g.ActiveId == state->ID && g.ActiveIdIsJustActivated); callback_data.UserData = callback_user_data; // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925 From 4252275c64b575257a004d204ed80fb0b420959a Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Mar 2026 20:10:14 +0100 Subject: [PATCH 05/37] InputTextMultiline: fixed an issue calculating lines count when inactive, no word-wrap, and ending with a \n. Amend 1e52e7b90c (#3237, #952, #1062, #7363) --- docs/CHANGELOG.txt | 3 +++ imgui.h | 2 +- imgui_widgets.cpp | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8828e28e..384863e5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -89,6 +89,9 @@ Other Changes: would be incorrect during the deactivation frame. (#9298) - Fixed a crash introduced in 1.92.6 when handling ImGuiInputTextFlags_CallbackResize in certain situations. (#9174) + - InputTextMultiline: fixed an issue introduced in 1.92.3 where line count calculated + for vertical scrollbar range would be +1 when the widget is inactive, word-wrap is + disabled and the text buffer ends with '\n'. - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness diff --git a/imgui.h b/imgui.h index 0086c60a..e4f4f5d8 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 19265 +#define IMGUI_VERSION_NUM 19266 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8a2ab9f3..3c980b86 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4588,6 +4588,7 @@ static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* li ImGuiContext& g = *GImGui; int size = 0; const char* s; + bool trailing_line_already_counted = false; if (flags & ImGuiInputTextFlags_WordWrap) { for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s) @@ -4608,6 +4609,7 @@ static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* li } else { + // Inactive path: we don't know buf_end ahead of time. const char* s_eol; for (s = buf; ; s = s_eol + 1) { @@ -4616,6 +4618,7 @@ static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* li if ((s_eol = strchr(s, '\n')) != NULL) continue; s += strlen(s); + trailing_line_already_counted = true; break; } } @@ -4626,7 +4629,7 @@ static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* li line_index->Offsets.push_back(0); size++; } - if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size) + if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size && !trailing_line_already_counted) { line_index->Offsets.push_back((int)(buf_end - buf)); size++; From b724f940d64430f26fbae67c675115dbb49ae4bc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 11:20:00 +0100 Subject: [PATCH 06/37] InputText: fixed selection highlight Y1 offset being very slightly off (since 1.92.3). (#9311) Fixes 1e52e7b90c0 --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 384863e5..1dcd0f8f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -89,6 +89,7 @@ Other Changes: would be incorrect during the deactivation frame. (#9298) - Fixed a crash introduced in 1.92.6 when handling ImGuiInputTextFlags_CallbackResize in certain situations. (#9174) + - Fixed selection highlight Y1 offset being very slightly off (since 1.92.3). (#9311) [@v-ein] - InputTextMultiline: fixed an issue introduced in 1.92.3 where line count calculated for vertical scrollbar range would be +1 when the widget is inactive, word-wrap is disabled and the text buffer ends with '\n'. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3c980b86..f39469c4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5532,7 +5532,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (render_selection) { const ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests. - const float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + const float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME-DPI: those offsets should be part of the style? they don't play so well with multi-line selection. const float bg_offy_dn = is_multiline ? 0.0f : 2.0f; const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines @@ -5561,7 +5561,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize; rect.Max.x = rect.Min.x + rect_width; rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize; - rect.Min.y -= bg_offy_up; + rect.Min.y += bg_offy_up; rect.ClipWith(clip_rect); draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); } From 7fc3092870941483aa80e392822203543828a062 Mon Sep 17 00:00:00 2001 From: Starman <75682000+StarmanAkremis@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:06:13 +0000 Subject: [PATCH 07/37] Backends: SDLGPU3: Prevent DestroyTexture from deleting invalid textures if ImTextureID_Invalid != 0. (#9310, #9293) Amend 0db5919 --- backends/imgui_impl_sdlgpu3.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_sdlgpu3.cpp b/backends/imgui_impl_sdlgpu3.cpp index 78392305..75a2b739 100644 --- a/backends/imgui_impl_sdlgpu3.cpp +++ b/backends/imgui_impl_sdlgpu3.cpp @@ -310,8 +310,9 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe static void ImGui_ImplSDLGPU3_DestroyTexture(ImTextureData* tex) { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - if (SDL_GPUTexture* raw_tex = (SDL_GPUTexture*)(intptr_t)tex->GetTexID()) - SDL_ReleaseGPUTexture(bd->InitInfo.Device, raw_tex); + if (tex->GetTexID() != ImTextureID_Invalid) + if (SDL_GPUTexture* raw_tex = (SDL_GPUTexture*)(intptr_t)tex->GetTexID()) + SDL_ReleaseGPUTexture(bd->InitInfo.Device, raw_tex); // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) tex->SetTexID(ImTextureID_Invalid); From 0500e546b57a15f232aca3795022059cbadeb5a4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 11:47:01 +0100 Subject: [PATCH 08/37] Backends: DX9, Metal, SDLRenderer2/3: fixed more assumptions that ImTextureID_Invald == 0 + Amend Changelogs. (#9310, #9293) --- backends/imgui_impl_dx9.cpp | 16 +++++++++------- backends/imgui_impl_metal.mm | 4 +++- backends/imgui_impl_opengl2.cpp | 1 + backends/imgui_impl_opengl3.cpp | 1 + backends/imgui_impl_sdlgpu3.cpp | 1 + backends/imgui_impl_sdlrenderer2.cpp | 6 ++++-- backends/imgui_impl_sdlrenderer3.cpp | 6 ++++-- docs/CHANGELOG.txt | 2 ++ 8 files changed, 25 insertions(+), 12 deletions(-) diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 37b5815c..81f14215 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-19: Fixed issue in ImGui_ImplDX9_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: DirectX9: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. // 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap. @@ -431,14 +432,15 @@ void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy) { - if (LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)tex->TexID) - { - IM_ASSERT(tex->TexID == (ImTextureID)(intptr_t)backend_tex); - backend_tex->Release(); + if (tex->ID != ImTextureID_Invalid) + if (LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)tex->TexID) + { + IM_ASSERT(tex->TexID == (ImTextureID)(intptr_t)backend_tex); + backend_tex->Release(); - // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) - tex->SetTexID(ImTextureID_Invalid); - } + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + } tex->SetStatus(ImTextureStatus_Destroyed); } } diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 96695d9b..0ab182a8 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-19: Fixed issue in ImGui_ImplMetal_RenderDrawData() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 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) @@ -307,7 +308,8 @@ void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, id [commandEncoder setScissorRect:scissorRect]; // Bind texture, Draw - if (ImTextureID tex_id = pcmd->GetTexID()) + ImTextureID tex_id = pcmd->GetTexID(); + if (tex_id != ImTextureID_Invalid) [commandEncoder setFragmentTexture:(__bridge id)(void*)(intptr_t)(tex_id) atIndex:0]; [commandEncoder setVertexBufferOffset:(vertexBufferOffset + pcmd->VtxOffset * sizeof(ImDrawVert)) atIndex:0]; diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 7b860098..4fffc577 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -25,6 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-07-15: OpenGL: Set GL_UNPACK_ALIGNMENT to 1 before updating textures. (#8802) // 2025-06-11: OpenGL: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplOpenGL2_CreateFontsTexture() and ImGui_ImplOpenGL2_DestroyFontsTexture(). diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 893e8b85..d1894a13 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-07-22: OpenGL: Add and call embedded loader shutdown during ImGui_ImplOpenGL3_Shutdown() to facilitate multiple init/shutdown cycles in same process. (#8792) diff --git a/backends/imgui_impl_sdlgpu3.cpp b/backends/imgui_impl_sdlgpu3.cpp index 75a2b739..54a527a3 100644 --- a/backends/imgui_impl_sdlgpu3.cpp +++ b/backends/imgui_impl_sdlgpu3.cpp @@ -22,6 +22,7 @@ // Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. // CHANGELOG +// 2026-03-19: Fixed issue in ImGui_ImplSDLGPU3_DestroyTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 2026-02-25: Removed unnecessary call to SDL_WaitForGPUIdle when releasing vertex/index buffers. (#9262) // 2025-11-26: macOS version can use MSL shaders in order to support macOS 10.14+ (vs Metallib shaders requiring macOS 14+). Requires calling SDL_CreateGPUDevice() with SDL_GPU_SHADERFORMAT_MSL. // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp index e03d1311..6d41a2b3 100644 --- a/backends/imgui_impl_sdlrenderer2.cpp +++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -24,6 +24,7 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer2_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer2_CreateFontsTexture() and ImGui_ImplSDLRenderer2_DestroyFontsTexture(). // 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color. @@ -267,8 +268,9 @@ void ImGui_ImplSDLRenderer2_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy) { - if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) - SDL_DestroyTexture(sdl_texture); + if (tex->ID != ImTextureID_Invalid) + if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) + SDL_DestroyTexture(sdl_texture); // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) tex->SetTexID(ImTextureID_Invalid); diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 72bc174e..b9c1d3fa 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -24,6 +24,7 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2026-03-12: Fixed invalid assert in ImGui_ImplSDLRenderer3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture(). // 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color. @@ -283,8 +284,9 @@ void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy) { - if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) - SDL_DestroyTexture(sdl_texture); + if (tex->ID != ImTextureID_Invalid) + if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) + SDL_DestroyTexture(sdl_texture); // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) tex->SetTexID(ImTextureID_Invalid); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1dcd0f8f..fcdcd08b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -134,6 +134,8 @@ Other Changes: collected when unused for io.ConfigMemoryCompactTimer amount of time. (#9303) - Demo: fixed IMGUI_DEMO_MARKER locations for examples applets. (#9261, #3689) [@pthom] - Backends: + - DirectX9, OpenGL2, OpenGL3, Metal, SDLGPU3, SDLRenderer2, SDLRenderer3: fixed easy-to-fix + issues in code assuming ImTextureID_Invalid is always defined to 0. (#9295, #9310) - SDLGPU3: removed unnecessary call to SDL_WaitForGPUIdle when releasing vertex/index buffers. (#9262) [@jaenis] - WebGPU: fixed version check for Emscripten 5.0.0+. From 4d1ba782eef4ff774d671a0d3bb48d31aa5412c0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 11:54:42 +0100 Subject: [PATCH 09/37] Revert changing default value of ImTextureID_Invalid to -1. Back to 0. (#9295, #9310, #9293, #8745, #8465, #7090) Reverts 0db591935f08c73f1e0726869a92ca803e8660a9 --- docs/CHANGELOG.txt | 10 ---------- imgui.cpp | 4 ---- imgui.h | 10 +++++----- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fcdcd08b..9966d851 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,16 +41,6 @@ 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. - - 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 1478018a..8de5a769 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,10 +395,6 @@ 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. - 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: diff --git a/imgui.h b/imgui.h index e4f4f5d8..f0f7c7c8 100644 --- a/imgui.h +++ b/imgui.h @@ -341,10 +341,10 @@ typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or #endif // 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. +// - If your backend is using ImTextureID to store an index/offset and you need 0 to be valid, You can add '#define ImTextureID_Invalid ((ImTextureID)-1)' in your imconfig.h file. +// - From 2026/03/12 to 2026/03/19 we experimented with changing to default to -1, but I worried it would cause too many issues in third-party code so it was reverted. #ifndef ImTextureID_Invalid -#define ImTextureID_Invalid ((ImTextureID)-1) +#define ImTextureID_Invalid ((ImTextureID)0) #endif // ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*. @@ -3909,8 +3909,8 @@ 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 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 ImTextureID_Invalid == 0 and your ImTextureID is used to store an index or an offset: + // - You can add '#define ImTextureID_Invalid ((ImTextureID)-1)' in your imconfig.h 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. From 358d3912c98ae6226f0e4c4584d8c41c3b1a7ffe Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 12:34:11 +0100 Subject: [PATCH 10/37] Backends: SDLRenderer2/3: fixed build, typo in 0500e54. --- backends/imgui_impl_sdlrenderer2.cpp | 2 +- backends/imgui_impl_sdlrenderer3.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp index 6d41a2b3..dc86f6c2 100644 --- a/backends/imgui_impl_sdlrenderer2.cpp +++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -268,7 +268,7 @@ void ImGui_ImplSDLRenderer2_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy) { - if (tex->ID != ImTextureID_Invalid) + if (tex->TexID != ImTextureID_Invalid) if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) SDL_DestroyTexture(sdl_texture); diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index b9c1d3fa..be6c97d8 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -284,7 +284,7 @@ void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy) { - if (tex->ID != ImTextureID_Invalid) + if (tex->TexID != ImTextureID_Invalid) if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) SDL_DestroyTexture(sdl_texture); From 20d8bcb600d6a0c54c911700a1a5c70765bdcc4c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 16:04:04 +0100 Subject: [PATCH 11/37] (Breaking) MultiSelect: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. (#1861, #6518) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 1 + imgui.h | 10 ++++++++-- imgui_demo.cpp | 8 ++++---- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9966d851..0c509600 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,6 +53,8 @@ Breaking Changes: 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. + - MultiSelect: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. + Kept inline redirection enum (will obsolete). - Combo(), ListBox(): commented out legacy signatures which were obsoleted in 1.90 (Nov 2023), when the getter callback type was changed from: getter type: bool (*getter)(void* user_data, int idx, const char** out_text) diff --git a/imgui.cpp b/imgui.cpp index 8de5a769..1cada18f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,6 +395,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/03/19 (1.92.7) - MultiSelect: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. - 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: diff --git a/imgui.h b/imgui.h index f0f7c7c8..f00ef968 100644 --- a/imgui.h +++ b/imgui.h @@ -3025,16 +3025,22 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5, // Disable clearing selection when clicking/selecting an already selected item. ImGuiMultiSelectFlags_BoxSelect1d = 1 << 6, // Enable box-selection with same width and same x pos items (e.g. full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space. ImGuiMultiSelectFlags_BoxSelect2d = 1 << 7, // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items. - ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, // Disable scrolling when box-selecting near edges of scope. + ImGuiMultiSelectFlags_BoxSelectNoScroll = 1 << 8, // Disable scrolling when box-selecting and moving mouse near edges of scope. ImGuiMultiSelectFlags_ClearOnEscape = 1 << 9, // Clear selection when pressing Escape while scope is focused. ImGuiMultiSelectFlags_ClearOnClickVoid = 1 << 10, // Clear selection when clicking on empty location within scope. ImGuiMultiSelectFlags_ScopeWindow = 1 << 11, // Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window. ImGuiMultiSelectFlags_ScopeRect = 1 << 12, // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window. - ImGuiMultiSelectFlags_SelectOnClick = 1 << 13, // Apply selection on mouse down when clicking on unselected item. (Default) + ImGuiMultiSelectFlags_SelectOnAuto = 1 << 13, // Apply selection on mouse down when clicking on unselected item, on mouse up when clicking on selected item. (Default) ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus. + ImGuiMultiSelectFlags_SelectOnMask_ = ImGuiMultiSelectFlags_SelectOnAuto | ImGuiMultiSelectFlags_SelectOnClickRelease, + + // Obsolete names +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiMultiSelectFlags_SelectOnClick = ImGuiMultiSelectFlags_SelectOnAuto, // RENAMED in 1.92.6 +#endif }; // Main IO structure returned by BeginMultiSelect()/EndMultiSelect(). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 738096c5..366d9c7d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3241,10 +3241,10 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d flags &= ~ImGuiMultiSelectFlags_ScopeRect; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect)) flags &= ~ImGuiMultiSelectFlags_ScopeWindow; - if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick)) - flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease; - if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) - flags &= ~ImGuiMultiSelectFlags_SelectOnClick; + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnAuto", &flags, ImGuiMultiSelectFlags_SelectOnAuto)) + flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnAuto); + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease)) + flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnClickRelease); ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); ImGui::TreePop(); } From 0b4967992a18e36c6da6fdbb8ce10fad7176ce2b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 16:29:03 +0100 Subject: [PATCH 12/37] MultiSelect: Box-Select: removed now seemingly unnecessary 'selected==false' check, which will also prevent implementation of ImGuiMultiSelectFlags_SelectOnClickAlways. (#9307) We enter into the block either though navigation, and then the Mouse check fails, either through mouse, and then Selected==false is tested above. Amend f904a6646. --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f39469c4..e39d614e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8293,7 +8293,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed) // Box-select ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) - if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) + if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1) BoxSelectPreStartDrag(ms->BoxSelectId, item_data); //---------------------------------------------------------------------------------------- From 9700846bb320d7f3670f3b2cc747058965d3ec1b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 16:38:13 +0100 Subject: [PATCH 13/37] MultiSelect: added ImGuiMultiSelectFlags_SelectOnClickAlways mode. Prevents Drag and Drop of multiple items but allows BoxSelect to always reselect even when clicking inside a selecttion. (#9307, #1861) --- docs/CHANGELOG.txt | 6 +++++- imgui.h | 5 +++-- imgui_demo.cpp | 22 ++++++++++++++++------ imgui_widgets.cpp | 9 ++++++--- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0c509600..8185b86c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,7 +53,7 @@ Breaking Changes: 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. - - MultiSelect: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. + - Multi-Select: renamed ImGuiMultiSelectFlags_SelectOnClick to ImGuiMultiSelectFlags_SelectOnAuto. Kept inline redirection enum (will obsolete). - Combo(), ListBox(): commented out legacy signatures which were obsoleted in 1.90 (Nov 2023), when the getter callback type was changed from: @@ -105,6 +105,10 @@ Other Changes: BeginPopupContextItem(), BeginPopupContextWindow() or OpenPopupOnItemClick(). (#8803, #9270) [@exelix11, @ocornut] - Popups: pressing North button (PS4/PS5 triangle, SwitchX, Xbox Y) also open popups menus. +- Multi-Select: + - Added ImGuiMultiSelectFlags_SelectOnClickAlways mode (rarely used). + This prevents Drag and Drop of multiple items, but it allows to start a new Box-Selection + from inside an existing selection (Excel style). (#9307, #1861) - 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.h b/imgui.h index f00ef968..eef153c6 100644 --- a/imgui.h +++ b/imgui.h @@ -3031,11 +3031,12 @@ enum ImGuiMultiSelectFlags_ ImGuiMultiSelectFlags_ScopeWindow = 1 << 11, // Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window. ImGuiMultiSelectFlags_ScopeRect = 1 << 12, // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window. ImGuiMultiSelectFlags_SelectOnAuto = 1 << 13, // Apply selection on mouse down when clicking on unselected item, on mouse up when clicking on selected item. (Default) - ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 14, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. + ImGuiMultiSelectFlags_SelectOnClickAlways = 1 << 14, // Apply selection on mouse down when clicking on any items. Prevents Drag and Drop from being used on multiple-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection. (Excel style behavior) + ImGuiMultiSelectFlags_SelectOnClickRelease = 1 << 15, // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection. //ImGuiMultiSelectFlags_RangeSelect2d = 1 << 15, // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does. ImGuiMultiSelectFlags_NavWrapX = 1 << 16, // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one. ImGuiMultiSelectFlags_NoSelectOnRightClick = 1 << 17, // Disable default right-click processing, which selects item on mouse down, and is designed for context-menus. - ImGuiMultiSelectFlags_SelectOnMask_ = ImGuiMultiSelectFlags_SelectOnAuto | ImGuiMultiSelectFlags_SelectOnClickRelease, + ImGuiMultiSelectFlags_SelectOnMask_ = ImGuiMultiSelectFlags_SelectOnAuto | ImGuiMultiSelectFlags_SelectOnClickAlways | ImGuiMultiSelectFlags_SelectOnClickRelease, // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 366d9c7d..c95fa8bb 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2632,7 +2632,7 @@ struct ExampleDualListBox } if (child_visible) { - ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None; + ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_BoxSelect1d; ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size); ApplySelectionRequests(ms_io, side); @@ -3243,6 +3243,10 @@ static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_d flags &= ~ImGuiMultiSelectFlags_ScopeWindow; if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnAuto", &flags, ImGuiMultiSelectFlags_SelectOnAuto)) flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnAuto); + ImGui::SameLine(); HelpMarker("Apply selection on mouse down when clicking on unselected item, on mouse up when clicking on selected item. (Default)"); + if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickAlways", &flags, ImGuiMultiSelectFlags_SelectOnClickAlways)) + flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnClickAlways); + ImGui::SameLine(); HelpMarker("Prevents Drag and Drop from being used on multi-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection. (Excel style behavior)"); if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease)) flags &= ~(ImGuiMultiSelectFlags_SelectOnMask_ ^ ImGuiMultiSelectFlags_SelectOnClickRelease); ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection."); @@ -10716,8 +10720,9 @@ struct ExampleAssetsBrowser // Options bool ShowTypeOverlay = true; bool AllowSorting = true; - bool AllowDragUnselected = false; - bool AllowBoxSelect = true; + bool AllowBoxSelect = true; // Will set ImGuiMultiSelectFlags_BoxSelect2d + bool AllowBoxSelectInsideSelection = false; // Will set ImGuiMultiSelectFlags_SelectOnClickAlways + bool AllowDragUnselected = false; // Will set ImGuiMultiSelectFlags_SelectOnClickRelease float IconSize = 32.0f; int IconSpacing = 10; int IconHitSpacing = 4; // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer. @@ -10822,8 +10827,11 @@ struct ExampleAssetsBrowser ImGui::Checkbox("Allow Sorting", &AllowSorting); ImGui::SeparatorText("Selection Behavior"); - ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected); ImGui::Checkbox("Allow box-selection", &AllowBoxSelect); + if (ImGui::Checkbox("Allow box-selection from selected items", &AllowBoxSelectInsideSelection) && AllowBoxSelectInsideSelection) + AllowDragUnselected = false; + if (ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected) && AllowDragUnselected) + AllowBoxSelectInsideSelection = false; ImGui::SeparatorText("Layout"); ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f"); @@ -10879,9 +10887,11 @@ struct ExampleAssetsBrowser if (AllowBoxSelect) ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d; - // - This feature allows dragging an unselected item without selecting it (rarely used) + // - Selection mode if (AllowDragUnselected) - ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; + ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease; // Rarely used: Allows dragging an unselected item without selecting it(rarely used) + else if (AllowBoxSelectInsideSelection) + ms_flags |= ImGuiMultiSelectFlags_SelectOnClickAlways; // Rarely used: Prevents Drag and Drop from being used on multiple-selection, but allows e.g. BoxSelect to always reselect even when clicking inside an existing selection. // - Enable keyboard wrapping on X axis // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing: diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e39d614e..936de909 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8170,10 +8170,13 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags { ImGuiButtonFlags button_flags = *p_button_flags; button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; - if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease)) - button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease; - else + button_flags &= ~(ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickRelease); + if (ms->Flags & ImGuiMultiSelectFlags_SelectOnClickAlways) + button_flags |= ImGuiButtonFlags_PressedOnClick; + else if (ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease) button_flags |= ImGuiButtonFlags_PressedOnClickRelease; + else // ImGuiMultiSelectFlags_SelectOnAuto + button_flags |= (!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnClickRelease; *p_button_flags = button_flags; } } From b2c3e37d55ee03e7f4cd6ad3f8d3cad333097cc0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 16:58:14 +0100 Subject: [PATCH 14/37] Multi-Select: fix/amend 9700846. . (#9307, #1861) --- imgui_widgets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 936de909..7e51511d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -8170,7 +8170,7 @@ void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags { ImGuiButtonFlags button_flags = *p_button_flags; button_flags |= ImGuiButtonFlags_NoHoveredOnFocus; - button_flags &= ~(ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickRelease); + button_flags &= ~(ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease); if (ms->Flags & ImGuiMultiSelectFlags_SelectOnClickAlways) button_flags |= ImGuiButtonFlags_PressedOnClick; else if (ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease) From 386ce49c58393d469fc477340e7725cb4170f50c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Mar 2026 18:17:35 +0100 Subject: [PATCH 15/37] Backends: DirectX9: fixed build typo in 0500e54. --- backends/imgui_impl_dx9.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 81f14215..24911fad 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -432,7 +432,7 @@ void ImGui_ImplDX9_UpdateTexture(ImTextureData* tex) } else if (tex->Status == ImTextureStatus_WantDestroy) { - if (tex->ID != ImTextureID_Invalid) + if (tex->TexID != ImTextureID_Invalid) if (LPDIRECT3DTEXTURE9 backend_tex = (LPDIRECT3DTEXTURE9)tex->TexID) { IM_ASSERT(tex->TexID == (ImTextureID)(intptr_t)backend_tex); From 3a26b640b2f2a4b9a9aa1e176cb967ca54c88cec Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 11:50:05 +0100 Subject: [PATCH 16/37] Drag and Drop: make SetDragDropPayload() memcpy size match our buffer. --- imgui.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 1cada18f..26518852 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14931,14 +14931,14 @@ bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_s // Store in heap g.DragDropPayloadBufHeap.resize((int)data_size); payload.Data = g.DragDropPayloadBufHeap.Data; - memcpy(payload.Data, data, data_size); + memcpy(payload.Data, data, (size_t)(int)data_size); } else if (data_size > 0) { // Store locally memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); payload.Data = g.DragDropPayloadBufLocal; - memcpy(payload.Data, data, data_size); + memcpy(payload.Data, data, (size_t)(int)data_size); } else { From 763db046fa25517b8ab4627f9188a5cdbea5ea51 Mon Sep 17 00:00:00 2001 From: Pascal Thomet Date: Fri, 20 Mar 2026 12:40:32 +0100 Subject: [PATCH 17/37] Docs: fixed imgui_manual -> imgui_explorer link. (#9315) --- imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.h b/imgui.h index eef153c6..32e4a814 100644 --- a/imgui.h +++ b/imgui.h @@ -20,7 +20,7 @@ // - Software using Dear ImGui https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui // - Issues & support ........... https://github.com/ocornut/imgui/issues // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) -// - Web version of the Demo .... https://pthom.github.io/imgui_manual (w/ source code browser) +// - Web version of the Demo .... https://pthom.github.io/imgui_explorer (w/ source code browser) // For FIRST-TIME users having issues compiling/linking/running: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. From 325563a982c0c2c889f02e771c82a848ed78ab22 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 15:17:07 +0100 Subject: [PATCH 18/37] InputTextMultiline: InputTextMultiline: fixed an issue calculating lines count when active. Amend 4252275 --- docs/CHANGELOG.txt | 3 ++- imgui.cpp | 2 +- imgui_widgets.cpp | 5 +---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8185b86c..71a1bb17 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -84,7 +84,8 @@ Other Changes: - Fixed selection highlight Y1 offset being very slightly off (since 1.92.3). (#9311) [@v-ein] - InputTextMultiline: fixed an issue introduced in 1.92.3 where line count calculated for vertical scrollbar range would be +1 when the widget is inactive, word-wrap is - disabled and the text buffer ends with '\n'. + disabled and the text buffer ends with '\n'. Fixed a similar issue related to clipping + large amount of text. - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness diff --git a/imgui.cpp b/imgui.cpp index 26518852..8e760d05 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5565,7 +5565,7 @@ void ImGui::NewFrame() // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves. if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId) { - IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n"); + IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() 0x%08X because it isn't marked alive anymore!\n", g.ActiveId); ClearActiveID(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7e51511d..c7159c12 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4629,11 +4629,8 @@ static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* li line_index->Offsets.push_back(0); size++; } - if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size && !trailing_line_already_counted) - { + if (buf_end > buf && buf_end[-1] == '\n' && !trailing_line_already_counted && size++ <= max_output_buffer_size) line_index->Offsets.push_back((int)(buf_end - buf)); - size++; - } return size; } From 2d957152e4347c214098b952080e0c52de4f50c3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 15:37:56 +0100 Subject: [PATCH 19/37] InputTextMultiline: avoid going through reactivation path and InputTextDeactivateHook() when activating scrollbar. (#9308) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 71a1bb17..ae3eb97b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -86,6 +86,7 @@ Other Changes: for vertical scrollbar range would be +1 when the widget is inactive, word-wrap is disabled and the text buffer ends with '\n'. Fixed a similar issue related to clipping large amount of text. + - InputTextMultiline: avoid going through reactivation code when activating scrollbar. - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c7159c12..096b3f94 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4770,8 +4770,9 @@ 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); 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); + const ImGuiID scrollbar_id = (is_multiline && state != NULL) ? GetWindowScrollbarID(draw_window, ImGuiAxis_Y) : 0; + const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == scrollbar_id; + const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == scrollbar_id; bool clear_active_id = false; bool select_all = false; @@ -4780,7 +4781,6 @@ 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 || input_requested_by_reactivate); - const bool init_state = (init_make_active || user_scroll_active); if (init_reload_from_user_buf) { int new_len = (int)ImStrlen(buf); @@ -4793,7 +4793,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->Stb->select_start = state->ReloadSelectionStart; state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; // will be clamped to bounds below } - else if ((init_state && g.ActiveId != id) || init_changed_specs) + else if ((init_make_active && g.ActiveId != id) || init_changed_specs) { // Access state even if we don't own it yet. state = &g.InputTextState; @@ -4903,7 +4903,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ClearActiveID(); // Release focus when we click outside - if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560 + if (g.ActiveId == id && io.MouseClicked[0] && !init_make_active) //-V560 clear_active_id = true; // Lock the decision of whether we are going to take the path displaying the cursor or selection From 04dfcd838b6c95381ce4cc4a3704bfc75cc81fd3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 15:39:02 +0100 Subject: [PATCH 20/37] InputTextMultiline: fixed losing revert value when activating scrollbar. (toward #9308) --- docs/CHANGELOG.txt | 3 ++- imgui_widgets.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ae3eb97b..8fabf796 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -86,7 +86,8 @@ Other Changes: for vertical scrollbar range would be +1 when the widget is inactive, word-wrap is disabled and the text buffer ends with '\n'. Fixed a similar issue related to clipping large amount of text. - - InputTextMultiline: avoid going through reactivation code when activating scrollbar. + - InputTextMultiline: avoid going through reactivation code and fixed losing revert value + when activating scrollbar. - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 096b3f94..8898e69a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4806,8 +4806,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) const int buf_len = (int)ImStrlen(buf); IM_ASSERT(((buf_len + 1 <= buf_size) || (buf_len == 0 && buf_size == 0)) && "Is your input buffer properly zero-terminated?"); - state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. - memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); + if (!user_scroll_finish) + { + state->TextToRevertTo.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->TextToRevertTo.Data, buf, buf_len + 1); + } // Preserve cursor position and undo/redo stack if we come back to same widget // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? From 2315b9f33dff22e3af7eee1192f529ad67d3a785 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 15:47:26 +0100 Subject: [PATCH 21/37] InputTextMultiline: fixed an issue where edit buffer wouldn't be reapplied to back buffer on the IsItemDeactivatedAfterEdit() frame. (#9308, #8915, #8273) --- docs/CHANGELOG.txt | 4 ++++ imgui_widgets.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8fabf796..6606c10d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -88,6 +88,10 @@ Other Changes: large amount of text. - InputTextMultiline: avoid going through reactivation code and fixed losing revert value when activating scrollbar. + - InputTextMultiline: fixed an issue where edit buffer wouldn't be reapplied to back + buffer on the IsItemDeactivatedAfterEdit() frame. This could create issues when + using the idiom of not applying edits before IsItemDeactivatedAfterEdit(). + (#9308, #8915, #8273) - Style: - Border sizes are now scaled (and rounded) by ScaleAllSizes(). - When using large values with ScallAllSizes(), the following items thickness diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8898e69a..3022950e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4731,6 +4731,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it. draw_window->DC.CursorPos += style.FramePadding; inner_size.x -= draw_window->ScrollbarSizes.x; + + // FIXME: Could this be a ImGuiChildFlags to affect the SetLastItemDataForWindow() call? + g.LastItemData.ID = id; + g.LastItemData.ItemFlags = item_data_backup.ItemFlags; + g.LastItemData.StatusFlags = item_data_backup.StatusFlags; } else { From 4af77622d93dbd9d8752eef3dd347f598de04fc6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 16:14:17 +0100 Subject: [PATCH 22/37] Scrollbar: Fixed an issue which could lead initial click to move the current scroll by a pixel. --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6606c10d..ec8abd87 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -124,6 +124,7 @@ 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) + - Fixed an issue which could lead initial click to move the current scroll by a pixel. - Button: - Moved ImGuiButtonFlags_AllowOverlap from imgui_internal.h to imgui.h, as a convenience for when using e.g. InvisibleButton(). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3022950e..11773334 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1042,7 +1042,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1); const float grab_h_minsize = ImMin(bb.GetSize()[axis], style.GrabMinSize); - const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), grab_h_minsize, scrollbar_size_v); + const float grab_h_pixels = (float)(int)ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), grab_h_minsize, scrollbar_size_v); const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // As a special thing, we allow scrollbar near the edge of a screen/viewport to be reachable with mouse at the extreme edge (#9276) From 50b488765fa1eab047dd56a3a1dc65fa3e6fa15d Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 20:05:24 +0100 Subject: [PATCH 23/37] ButtonBehavior, Selectable: made low-level ImGuiButtonFlags_PressedOnRelease not explicitely avoid taking current active id. ImGuiButtonFlags_NoHoldingActiveId may be used for that. Ditto for ImGuiSelectableFlags_SelectOnRelease, ImGuiSelectableFlags_NoHoldingActiveId. All internals. Toward #9312 --- docs/CHANGELOG.txt | 5 +++++ imgui.h | 2 +- imgui_internal.h | 2 +- imgui_widgets.cpp | 10 +++++++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ec8abd87..4af190ae 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -137,6 +137,11 @@ Other Changes: - Fixed GetForegroundDrawList()/GetBackgroundDrawList() per-viewport buffers not being collected when unused for io.ConfigMemoryCompactTimer amount of time. (#9303) - Demo: fixed IMGUI_DEMO_MARKER locations for examples applets. (#9261, #3689) [@pthom] +- Internals: + - ButtonBehavior: fixed internal/low-level ImGuiButtonFlags_PressedOnRelease + (as well as equivalent ImGuiSelectableFlags_SelectOnRelease for Selectable) from + not taking current active id. ImGuiButtonFlags_NoHoldingActiveID allows that. + This was pretty sure only used internally by MenuItem(). - Backends: - DirectX9, OpenGL2, OpenGL3, Metal, SDLGPU3, SDLRenderer2, SDLRenderer3: fixed easy-to-fix issues in code assuming ImTextureID_Invalid is always defined to 0. (#9295, #9310) diff --git a/imgui.h b/imgui.h index 32e4a814..875f40f8 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 19266 +#define IMGUI_VERSION_NUM 19267 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_internal.h b/imgui_internal.h index 3dbcfd96..b1ae63f2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1033,7 +1033,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnClick = 1 << 4, // return true on click (mouse down event) ImGuiButtonFlags_PressedOnClickRelease = 1 << 5, // [Default] return true on click + release on same item <-- this is what the majority of Button are using ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item - ImGuiButtonFlags_PressedOnRelease = 1 << 7, // return true on release (default requires click+release) + ImGuiButtonFlags_PressedOnRelease = 1 << 7, // return true on release (default requires click+release). Prior to 2026/03/20 this implied ImGuiButtonFlags_NoHoldingActiveId but they are separate now. ImGuiButtonFlags_PressedOnDoubleClick = 1 << 8, // return true on double-click (default requires click+release) ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) //ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat -> use ImGuiItemFlags_ButtonRepeat instead. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 11773334..fec9075a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -653,6 +653,14 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child } } + if (flags & ImGuiButtonFlags_PressedOnRelease) + { + // FIXME: Traditionally ImGuiButtonFlags_PressedOnRelease never took ActiveId. Adding it in 2026-03-20 since ImGuiButtonFlags_NoHoldingActiveId can always be added. + // We don't yet perform an explicit ClearActiveID() to reduce scope of change, but this possibility could be investigated. + if (!(flags & ImGuiButtonFlags_NoHoldingActiveId)) + SetActiveID(id, window); // Hold on ID + g.ActiveIdMouseButton = (ImS8)mouse_button_clicked; + } } if (flags & ImGuiButtonFlags_PressedOnRelease) { @@ -9458,7 +9466,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { From 8314fc3e5a10f7c6b670225065fce1dc8cfd396b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Mar 2026 20:47:07 +0100 Subject: [PATCH 24/37] Menus: shallow tweaks. --- imgui_widgets.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index fec9075a..b2020916 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9253,11 +9253,12 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) PushID(label); if (!enabled) BeginDisabled(); - const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + bool pressed; // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; + ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside a horizontal menu bar @@ -9265,9 +9266,8 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); - float w = label_size.x; ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, pos.y + window->DC.CurrLineTextBaseOffset); - pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); + pressed = Selectable("", menu_is_open, selectable_flags, label_size); LogSetNextTextDecoration("[", "]"); RenderText(text_pos, label); PopStyleVar(); @@ -9281,7 +9281,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.) float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); - float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame + float min_w = offsets->DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); ImVec2 text_pos(window->DC.CursorPos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); @@ -9467,16 +9467,15 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; - const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; + ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. - float w = label_size.x; window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f); - pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); + pressed = Selectable("", selected, selectable_flags, ImVec2(label_size.x, 0.0f)); PopStyleVar(); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) RenderText(text_pos, label); @@ -9490,7 +9489,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); - float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame + float min_w = offsets->DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); ImVec2 text_pos(pos.x, pos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); From 697b6886e33e8a818282ed973b2bb1d4ef6a5a83 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Mar 2026 14:13:30 +0100 Subject: [PATCH 25/37] Docs: update FAQ about label/ID system. (#9318) --- docs/FAQ.md | 122 +++++++++++++++++++++++++++++++++++++++++----------- imgui.cpp | 8 ++-- imgui.h | 2 +- 3 files changed, 103 insertions(+), 29 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 05dd777b..f81fb8b0 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -25,7 +25,7 @@ or view this file with any Markdown viewer. | [I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-clipping-or-disappearing-when-i-move-windows-around) | | [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | | **Q&A: Usage** | -| **[About the ID Stack system..
Why is my widget not reacting when I click on it?
Why is the wrong widget reacting when I click on one?
How can I have widgets with an empty label?
How can I have multiple widgets with the same label?
How can I have multiple windows with the same label?](#q-about-the-id-stack-system)** | +| **[About the ID Stack system...](#q-about-the-id-stack-system)**
**[How can I have multiple widgets with the same label?](#q-how-can-i-have-multiple-widgets-with-the-same-label) (using `##` or `PushID()`)**
**[How can I have widgets with an empty label?](#q-how-can-i-have-widgets-with-an-empty-label) (using `##`)**
**[How can I animate the label of an existing widget?](#q-how-can-i-change-the-label-of-an-existing-widget) (using `###`)**
**[General description of the label and ID Stack system.](#general-description-of-the-label-and-id-stack-system)** | | [How can I display an image?](#q-how-can-i-display-an-image)
[What are ImTextureID/ImTextureRef?](#q-what-are-imtextureidimtextureref)| | [How can I use maths operators with ImVec2?](#q-how-can-i-use-maths-operators-with-imvec2) | | [How can I use my own maths types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-maths-types-instead-of-imvec2imvec4) | @@ -263,15 +263,19 @@ ctx->RSSetScissorRects(1, &r); # Q&A: Usage -### Q: About the ID Stack system... -### Q: Why is my widget not reacting when I click on it? -### Q: Why is the wrong widget reacting when I click on one? -### Q: How can I have widgets with an empty label? -### Q: How can I have multiple widgets with the same label? -### Q: How can I have multiple windows with the same label? +## Q: About the ID Stack system... **USING THE SAME LABEL+ID IS THE MOST COMMON USER MISTAKE!**
**USING AN EMPTY LABEL IS THE SAME AS USING THE SAME LABEL AS YOUR PARENT WIDGET!** +
Read the questions in this section for a more general understand of how labels and ID works in Dear ImGui. + +TL;DR; +- Widgets labels are also used to compute Widgets unique identifiers. +- Unique identifiers are hashes of the label + of the parent scope (e.g. parent window or parent tree node labels). +- You can use `PushID()` to append to the identifier without making it visible. +- You can use `"##something"` in a label to append to the identifier without making it visible. +- You can use `"###something"` in a label to make the identifier ignore the visible part. + @@ -302,16 +306,99 @@ ImGui::End();
-A primer on labels and the ID Stack... +### Q: How can I have multiple widgets with the same label? + +A. When widgets are in a same scope and finite, you can use a `"##something"` suffix which will be part of the identifier but not visible as a label. + +```cpp +Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") +Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from other buttons +Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from other buttons +``` + +B. More generally, e.g. in loops, you can use `PushID()/PopID()` to push a prefix which will be part of the identifier. + +```cpp +// Using PushID() with a string +for (int i = 0; i < 100; i++) +{ + MyObject* obj = Objects[i]; + PushID(obj->Name); + Button("Click"); // Label = "Click", ID = hash of ("Window", obj->Name, "Click") + PopID(); +} +``` + +```cpp +// Using PushID() with an index +for (int i = 0; i < 100; i++) +{ + PushID(i); + Button("Click"); // Label = "Click", ID = hash of ("Window", i, "Click") + PopID(); +} +``` + +### Q: How can I have widgets with an empty label? + +If you want to completely hide the label, but still need an ID: + +```cpp +Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! +``` +##### [Return to Index](#index) + +### Q: How can I make a label dynamic? + +Dear ImGui is very dynamic so you can submit different widgets every frame. +However, in order to preserve widget state (eg. which tree node is open; which button is focused) the library internaly refers to their unique ID. + +Occasionally you might want to change a label while preserving a constant ID. This allows you to change/animate labels while persisting associated state. +For example, you may want to include varying information in a window title bar or button label. + +Using "###" exclude the preceeding part from ID computation: +```cpp +Button("Hello###ID"); // Label = "Enable", ID = hash of (..., "ID") +Button("World###ID"); // Label = "Disable", ID = hash of (..., "ID") // Same ID, different label +``` +Using a same ID ensure that associated related e.g. weither the widget is focused, won't be lost when the label changes. + + + + + + +
+
+// Window label has animating FPS counter
+// Window ID stays the same = hash of "MyGame"
+char buf[128];
+sprintf(buf, "My game (%.1f FPS)###MyGame", io.Framerate);
+ImGui::Begin(buf);
+ 
+// Label changes between "Enable" and "Disable"
+// ID stays the same = hash of ("MyGame", "MyButton")
+if (ImGui::Button(enabled ? "Disable###MyButton" : "Enable###MyButton", { -FLT_MIN, 0.0f }))
+    enabled = !enabled;
+ 
+ImGui::End();
+
+
+ +(Hint: I'd suggest wrapping sprintf in something more compact to use, e.g. [ocornut/Str](https://github.com/ocornut/Str) for what I personally use). + +##### [Return to Index](#index) + +### General description of the label and ID Stack system Dear ImGui internally needs to uniquely identify UI elements. -Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. -Interactive widgets (such as calls to Button buttons) need a unique ID. +Elements that are typically not clickable (such as calls to Text() functions) don't need an ID. +Interactive widgets (such as calls to Button() functions) need a unique ID. **Unique IDs are used internally to track active widgets and occasionally associate state to widgets.
Unique IDs are implicitly built from the hash of multiple elements that identify the "path" to the UI element.** -Since Dear ImGui 1.85, you can use `Demo>Tools>ID Stack Tool` or call `ImGui::ShowIDStackToolWindow()`. The tool display intermediate values leading to the creation of a unique ID, making things easier to debug and understand. +You can use `Demo>Tools>ID Stack Tool` or call `ImGui::ShowIDStackToolWindow()`. The tool display intermediate values leading to the creation of a unique ID, making things easier to debug and understand. ![Stack tool](https://user-images.githubusercontent.com/8225057/136235657-a0ea5665-dcd1-423f-9be6-dc3f8ced8f12.png) @@ -365,20 +452,7 @@ Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo Button("##foo"); // Label = "", ID = hash of ("MyWindow", "##foo") // Different from window End(); ``` -- If you want to completely hide the label, but still need an ID: -```cpp -Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label, just a checkbox! -``` -- Occasionally/rarely you might want to change a label while preserving a constant ID. This allows -you to animate labels. For example, you may want to include varying information in a window title bar, -but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: -```cpp -Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID") -Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same ID, different label -sprintf(buf, "My game (%f FPS)###MyGame", fps); -Begin(buf); // Variable title, ID = hash of "MyGame" -``` - Solving ID conflict in a more general manner: Use `PushID()` / `PopID()` to create scopes and manipulate the ID stack, as to avoid ID conflicts within the same window. This is the most convenient way of distinguishing ID when iterating and diff --git a/imgui.cpp b/imgui.cpp index 8e760d05..ac62bccd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1150,10 +1150,10 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: ---------- Q: About the ID Stack system.. - - Why is my widget not reacting when I click on it? - - How can I have widgets with an empty label? - - How can I have multiple widgets with the same label? - - How can I have multiple windows with the same label? + - How can I have multiple widgets with the same label? (using ## or PushID) + - How can I have widgets with an empty label? (using ##) + - How can I make a label dynamic? (using ###) + - General description of the label and ID Stack system. Q: How can I display an image? What is ImTextureID, how does it work? Q: How can I use my own math types instead of ImVec2? Q: How can I interact with standard C++ types (such as std::string and std::vector)? diff --git a/imgui.h b/imgui.h index 875f40f8..7aec6295 100644 --- a/imgui.h +++ b/imgui.h @@ -914,7 +914,7 @@ namespace ImGui // The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody. // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in // some advanced use cases (e.g. adding custom widgets in header row). - // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. + // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. When freezing columns you would usually also use ImGuiTableColumnFlags_NoHide on them. IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) From 38f5e5a0b839b19b78dadc0f3d25b7e8ecabbe5b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Mar 2026 14:42:26 +0100 Subject: [PATCH 26/37] Tables: rework column reordering code. (#9312) - Move more logic into TableQueueSetColumnDisplayOrder() so that it may be called from different locations. - Use to checking if both columns are on same size of the frozen barrier slightly changed to avoid reordering hidden column (with caveat of ill-defined design for what's "right"). --- imgui_internal.h | 3 ++- imgui_tables.cpp | 56 +++++++++++++++++++++++++++--------------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index b1ae63f2..b6a5d991 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3045,7 +3045,7 @@ struct IMGUI_API ImGuiTable ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. ImGuiTableColumnIdx HeldHeaderColumn; // Index of column header being held. ImGuiTableColumnIdx ReorderColumn; // Index of column being reordered. (not cleared) - ImGuiTableColumnIdx ReorderColumnDir; // -1 or +1 + ImGuiTableColumnIdx ReorderColumnDstOrder; // Requested display order of column being reordered. ImGuiTableColumnIdx LeftMostEnabledColumn; // Index of left-most non-hidden column. ImGuiTableColumnIdx RightMostEnabledColumn; // Index of right-most non-hidden column. ImGuiTableColumnIdx LeftMostStretchedColumn; // Index of left-most stretched column. @@ -3560,6 +3560,7 @@ namespace ImGui IMGUI_API void TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n); IMGUI_API void TableSetColumnWidthAutoAll(ImGuiTable* table); IMGUI_API void TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); + IMGUI_API void TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order); IMGUI_API void TableRemove(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTable* table); IMGUI_API void TableGcCompactTransientBuffers(ImGuiTableTempData* table); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index a51f2f01..de7380bb 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -702,21 +702,11 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) // Note: we don't clear ReorderColumn after handling the request (FIXME: clarify why or add a test). if (table->InstanceCurrent == 0) { - if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1) - table->ReorderColumn = -1; table->HeldHeaderColumn = -1; - if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0) + if (table->ReorderColumn != -1 && table->ReorderColumnDstOrder != -1) { - // We need to handle reordering across hidden columns. - // In the configuration below, moving C to the right of E will lead to: - // ... C [D] E ---> ... [D] E C (Column name/index) - // ... 2 3 4 ... 2 3 4 (Display order) - IM_ASSERT(table->ReorderColumnDir == -1 || table->ReorderColumnDir == +1); - IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable); - ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn]; - ImGuiTableColumn* dst_column = &table->Columns[(table->ReorderColumnDir < 0) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn]; - TableSetColumnDisplayOrder(table, table->ReorderColumn, dst_column->DisplayOrder); - table->ReorderColumnDir = 0; + TableSetColumnDisplayOrder(table, table->ReorderColumn, table->ReorderColumnDstOrder); + table->ReorderColumnDstOrder = -1; } } @@ -730,8 +720,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) } } -// Note that TableSetupScrollFreeze() enforce a display order range for frozen columns. -// So reordering a column across the frozen column barrier is illegal and will be undone. +// Apply immediately. See TableQueueSetColumnDisplayOrder() for additional checks/constraints. void ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order) { IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount); @@ -755,6 +744,25 @@ void ImGui::TableSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_ table->IsSettingsDirty = true; } +// Reorder requested by user indirection needs to verify +// - That we don't reorder columns with the ImGuiTableColumnFlags_NoReorder flag. +// - That we don't cross the frozen column limit. +// (that TableSetupScrollFreeze() enforce a display order range for frozen columns. +// so reordering a column across the frozen column barrier is illegal and will be undone.) +void ImGui::TableQueueSetColumnDisplayOrder(ImGuiTable* table, int column_n, int dst_order) +{ + ImGuiTableColumn* src_column = &table->Columns[column_n]; + ImGuiTableColumn* dst_column = &table->Columns[table->DisplayOrderToIndex[dst_order]]; + if ((src_column->Flags | dst_column->Flags) & ImGuiTableColumnFlags_NoReorder) // FIXME: Perform a sweep test? + return; + int src_i = (src_column->IndexWithinEnabledSet != -1) ? src_column->IndexWithinEnabledSet : table->Columns.index_from_ptr(src_column); // FIXME: Hidden columns don't count into the FreezeColumns count, so what to do here is ill-defined. For now we use regular index. + int dst_i = (dst_column->IndexWithinEnabledSet != -1) ? dst_column->IndexWithinEnabledSet : table->Columns.index_from_ptr(dst_column); + if ((src_i < table->FreezeColumnsRequest) != (dst_i < table->FreezeColumnsRequest)) + return; + table->ReorderColumn = (ImGuiTableColumnIdx)column_n; + table->ReorderColumnDstOrder = (ImGuiTableColumnIdx)dst_order; +} + // Adjust flags: default width mode + stretch columns are not allowed when auto extending static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in) { @@ -3224,21 +3232,19 @@ void ImGui::TableHeader(const char* label) // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone. if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive) { - // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x - table->ReorderColumn = (ImGuiTableColumnIdx)column_n; + // - While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x + // - We need to handle reordering across hidden columns. + // In the configuration below, moving C to the right of E will lead to: + // ... C [D] E ---> ... [D] E C (Column name/index) + // ... 2 3 4 ... 2 3 4 (Display order) + // - The other constraints are enforced by TableQueueSetColumnDisplayOrder() which might early out. table->InstanceInteracted = table->InstanceCurrent; - - // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder. if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x) if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL) - if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder)) - if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) - table->ReorderColumnDir = -1; + TableQueueSetColumnDisplayOrder(table, column_n, prev_column->DisplayOrder); if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x) if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL) - if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder)) - if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest)) - table->ReorderColumnDir = +1; + TableQueueSetColumnDisplayOrder(table, column_n, next_column->DisplayOrder); } // Sort order arrow From 6b1776a7d23b2c7c3750443e3a40be16a52549ba Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Mar 2026 15:37:56 +0100 Subject: [PATCH 27/37] Tables: context menu can be open using navigation Shift+F10 etc. (#8803, #9270) --- imgui_tables.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index de7380bb..3eef6b56 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3283,7 +3283,7 @@ void ImGui::TableHeader(const char* label) SetItemTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden - if (IsMouseReleased(1) && IsItemHovered()) + if (IsPopupOpenRequestForItem(ImGuiPopupFlags_None, id)) TableOpenContextMenu(column_n); } From 79411a04051d1a4d0abd79755fa36be9c69c8cc2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Mar 2026 16:12:39 +0100 Subject: [PATCH 28/37] Backends, Examples: WebGPU: fixed warnings + scale initial surface height for native. --- backends/imgui_impl_wgpu.cpp | 4 ++-- examples/example_glfw_wgpu/main.cpp | 8 +++++--- examples/example_sdl2_wgpu/main.cpp | 6 +++++- examples/example_sdl3_wgpu/main.cpp | 6 +++++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 2b7edb0b..acd8ae11 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -436,8 +436,8 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder } int64_t vb_write_size = MEMALIGN((char*)vtx_dst - (char*)fr->VertexBufferHost, 4); int64_t ib_write_size = MEMALIGN((char*)idx_dst - (char*)fr->IndexBufferHost, 4); - wgpuQueueWriteBuffer(bd->defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, vb_write_size); - wgpuQueueWriteBuffer(bd->defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, ib_write_size); + wgpuQueueWriteBuffer(bd->defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, (size_t)vb_write_size); + wgpuQueueWriteBuffer(bd->defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, (size_t)ib_write_size); // Setup desired render state ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index 54dfc690..cae04ac4 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -65,8 +65,8 @@ int main(int, char**) // Create window float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only - wgpu_surface_width *= main_scale; - wgpu_surface_height *= main_scale; + wgpu_surface_width = (int)(wgpu_surface_width * main_scale); + wgpu_surface_height = (int)(wgpu_surface_height * main_scale); GLFWwindow* window = glfwCreateWindow(wgpu_surface_width, wgpu_surface_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); if (window == nullptr) return 1; @@ -343,6 +343,7 @@ static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter #elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) { + IM_UNUSED(userdata2); if (status == WGPURequestAdapterStatus_Success) { WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1; @@ -356,6 +357,7 @@ static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2) { + IM_UNUSED(userdata2); if (status == WGPURequestDeviceStatus_Success) { WGPUDevice* extDevice = (WGPUDevice*)userdata1; @@ -549,7 +551,7 @@ WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window) } #else #error "Unsupported WebGPU native platform!" -#endif return nullptr; +#endif } #endif // #ifndef __EMSCRIPTEN__ diff --git a/examples/example_sdl2_wgpu/main.cpp b/examples/example_sdl2_wgpu/main.cpp index 8d7c5a10..85f8b39e 100644 --- a/examples/example_sdl2_wgpu/main.cpp +++ b/examples/example_sdl2_wgpu/main.cpp @@ -53,6 +53,8 @@ int main(int, char**) // Create window with graphics context float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); + wgpu_surface_width = (int)(wgpu_surface_width * main_scale); + wgpu_surface_height = (int)(wgpu_surface_height * main_scale); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+WebGPU example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, wgpu_surface_width, wgpu_surface_height, window_flags); if (window == nullptr) @@ -327,6 +329,7 @@ static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter #elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) { + IM_UNUSED(userdata2); if (status == WGPURequestAdapterStatus_Success) { WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1; @@ -340,6 +343,7 @@ static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2) { + IM_UNUSED(userdata2); if (status == WGPURequestDeviceStatus_Success) { WGPUDevice* extDevice = (WGPUDevice*)userdata1; @@ -514,7 +518,7 @@ WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window) } #else #error "Unsupported WebGPU native platform!" -#endif return nullptr; +#endif } #endif // #ifndef __EMSCRIPTEN__ diff --git a/examples/example_sdl3_wgpu/main.cpp b/examples/example_sdl3_wgpu/main.cpp index 523d6215..939cdfca 100644 --- a/examples/example_sdl3_wgpu/main.cpp +++ b/examples/example_sdl3_wgpu/main.cpp @@ -61,6 +61,8 @@ int main(int, char**) // Create SDL window graphics context float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE; + wgpu_surface_width = (int)(wgpu_surface_width * main_scale); + wgpu_surface_height = (int)(wgpu_surface_height * main_scale); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+WebGPU example", wgpu_surface_width, wgpu_surface_height, window_flags); if (window == nullptr) { @@ -338,6 +340,7 @@ static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter #elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) { + IM_UNUSED(userdata2); if (status == WGPURequestAdapterStatus_Success) { WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1; @@ -351,6 +354,7 @@ static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2) { + IM_UNUSED(userdata2); if (status == WGPURequestDeviceStatus_Success) { WGPUDevice* extDevice = (WGPUDevice*)userdata1; @@ -526,7 +530,7 @@ WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window) } #else #error "Unsupported WebGPU native platform!" -#endif return nullptr; +#endif } #endif // #ifndef __EMSCRIPTEN__ From 6c754ed2cb483488cf728a0d80f0152cbcb5eeb8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Mar 2026 16:21:31 +0100 Subject: [PATCH 29/37] TempInputText, InputText: enforce making active via ImGuiInputTextFlags_MergedItem. Restore cursor as Rect is provided + comments. (#2718, #6690) --- imgui.cpp | 2 ++ imgui_widgets.cpp | 21 ++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ac62bccd..a10d0a0c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11108,6 +11108,8 @@ bool ImGui::ErrorLog(const char* msg) return g.IO.ConfigErrorRecoveryEnableAssert; } +// Display an error tooltip when same ID as HoveredId was submitted multiple times. +// See code in ItemHoverable() for an explanation of why we associate this error to HoveredId + code drawing of rectangles over individual items instances. void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() { #ifndef IMGUI_DISABLE_DEBUG_TOOLS diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b2020916..376a53ca 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3694,27 +3694,33 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) } // Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets) +// - This must be submitted right after the item it is overlaying. +// - 'id' must be == 'window->GetID(label)'. See #2718 for a custom use of this. // FIXME: Facilitate using this in variety of other situations. -// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but -// the expected relationship between TempInputXXX functions and LastItemData is a little fishy. bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) { // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. // We clear ActiveID on the first frame to allow the InputText() taking it back. ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const bool init = (g.TempInputId != id); if (init) ClearActiveID(); - g.CurrentWindow->DC.CursorPos = bb.Min; - g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem); + ImVec2 backup_pos = window->DC.CursorPos; + window->DC.CursorPos = bb.Min; + g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; // Using ImGuiInputTextFlags_MergedItem above will skip ItemAdd() so we poke here. + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem | ImGuiInputTextFlags_AutoSelectAll); if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. IM_ASSERT(g.ActiveId == id); g.TempInputId = g.ActiveId; } + if (g.ActiveId != id) + g.TempInputId = 0; + window->DC.CursorPos = backup_pos; return value_changed; } @@ -4780,9 +4786,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_wordwrap) wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize)); + const bool user_clicked = hovered && io.MouseClicked[0]; const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive - const bool user_clicked = hovered && io.MouseClicked[0]; + const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_MergedItem)); const ImGuiID scrollbar_id = (is_multiline && state != NULL) ? GetWindowScrollbarID(draw_window, ImGuiAxis_Y) : 0; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == scrollbar_id; const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == scrollbar_id; @@ -4793,7 +4800,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 || input_requested_by_reactivate); + const bool init_make_active = (input_requested_by_user || input_requested_by_nav || input_requested_by_reactivate || user_scroll_finish); if (init_reload_from_user_buf) { int new_len = (int)ImStrlen(buf); From 51f590a2a83c2ae5b0aa44038abb72a5e5c99164 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 25 Mar 2026 14:17:29 +0100 Subject: [PATCH 30/37] Backends: GLFW: mouse cursor is properly restored if changed by user app/code while using glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. (#9322) Amend 9a4fd69f6 --- backends/imgui_impl_glfw.cpp | 4 ++++ docs/CHANGELOG.txt | 3 +++ 2 files changed, 7 insertions(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index da54589f..27f5fe90 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-25: Mouse cursor is properly restored if changed by user app/code while using glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. Amend change from 2025-12-10. // 2026-02-10: Try to set IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND automatically if corresponding headers are not accessible. (#9225) // 2025-12-12: Added IMGUI_IMPL_GLFW_DISABLE_X11 / IMGUI_IMPL_GLFW_DISABLE_WAYLAND to forcefully disable either. // 2025-12-10: Avoid repeated glfwSetCursor()/glfwSetInputMode() calls when unnecessary. Lowers overhead for very high framerates (e.g. 10k+ FPS). @@ -859,7 +860,10 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() ImGuiIO& io = ImGui::GetIO(); ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(bd->Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + { + bd->LastMouseCursor = nullptr; // Invalidate so that if user changes underlying cursor we will update it next time we can. return; + } ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4af190ae..e1ddbad9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -145,6 +145,9 @@ Other Changes: - Backends: - DirectX9, OpenGL2, OpenGL3, Metal, SDLGPU3, SDLRenderer2, SDLRenderer3: fixed easy-to-fix issues in code assuming ImTextureID_Invalid is always defined to 0. (#9295, #9310) + - GLFW: mouse cursor is properly restored if changed by user app/code while using + glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. + Amend optimization done in 1.92.6. - SDLGPU3: removed unnecessary call to SDL_WaitForGPUIdle when releasing vertex/index buffers. (#9262) [@jaenis] - WebGPU: fixed version check for Emscripten 5.0.0+. From 0b82487fed6b858e28efb80cbae4ca5440060a13 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 25 Mar 2026 14:29:54 +0100 Subject: [PATCH 31/37] TempInputText: amends. Rename ImGuiInputTextFlags_MergedItem to ImGuiInputTextFlags_TempInput for explicitness. (#2718) --- imgui_internal.h | 2 +- imgui_widgets.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index b6a5d991..cbdfc95b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1023,7 +1023,7 @@ enum ImGuiInputTextFlagsPrivate_ { // [Internal] ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() - ImGuiInputTextFlags_MergedItem = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. + ImGuiInputTextFlags_TempInput = 1 << 27, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28, // For internal use by InputScalar() and TempInputScalar() }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 376a53ca..c9edbe7c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3695,7 +3695,6 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) // Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets) // - This must be submitted right after the item it is overlaying. -// - 'id' must be == 'window->GetID(label)'. See #2718 for a custom use of this. // FIXME: Facilitate using this in variety of other situations. bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) { @@ -3711,7 +3710,8 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ImVec2 backup_pos = window->DC.CursorPos; window->DC.CursorPos = bb.Min; g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; // Using ImGuiInputTextFlags_MergedItem above will skip ItemAdd() so we poke here. - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem | ImGuiInputTextFlags_AutoSelectAll); + bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_TempInput | ImGuiInputTextFlags_AutoSelectAll); + KeepAliveID(id); // Not done because of ImGuiInputTextFlags_TempInput if (init) { // First frame we started displaying the InputText widget, we expect it to take the active id. @@ -4755,7 +4755,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd) ItemSize(total_bb, style.FramePadding.y); - if (!(flags & ImGuiInputTextFlags_MergedItem)) + if (!(flags & ImGuiInputTextFlags_TempInput)) if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) return false; } @@ -4789,7 +4789,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool user_clicked = hovered && io.MouseClicked[0]; const bool input_requested_by_nav = (g.ActiveId != id) && (g.NavActivateId == id); const bool input_requested_by_reactivate = (g.InputTextReactivateId == id); // for io.ConfigInputTextEnterKeepActive - const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_MergedItem)); + const bool input_requested_by_user = (user_clicked) || (g.ActiveId == 0 && (flags & ImGuiInputTextFlags_TempInput)); const ImGuiID scrollbar_id = (is_multiline && state != NULL) ? GetWindowScrollbarID(draw_window, ImGuiAxis_Y) : 0; const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == scrollbar_id; const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == scrollbar_id; From 59780020f647e398acfc64796dfde90451cb83a1 Mon Sep 17 00:00:00 2001 From: r-lyeh <35402248+r-lyeh@users.noreply.github.com> Date: Sat, 21 Mar 2026 00:36:24 +0100 Subject: [PATCH 32/37] Backends, Examples: WebGPU: added support for WGVK. (#9316, #9246, #9257) Squashed: minified SPIRV binaries by stripping strings + emitting in binary_to_compressed_c. --- backends/imgui_impl_wgpu.cpp | 89 +++++++++++++++++++++++++++-- backends/imgui_impl_wgpu.h | 3 +- examples/example_glfw_wgpu/main.cpp | 6 +- examples/example_sdl2_wgpu/main.cpp | 6 +- examples/example_sdl3_wgpu/main.cpp | 6 +- 5 files changed, 97 insertions(+), 13 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index acd8ae11..c7458e46 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -56,14 +56,14 @@ #include // One of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details. -#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) -#error Exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined! +#if !defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) +#error Exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN, IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_WGVK must be defined! #endif #if defined(__EMSCRIPTEN__) && defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #error Emscripten <4.0.10 with '-sUSE_WEBGPU=1' is not supported anymore. #endif -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN +#if defined IMGUI_IMPL_WEBGPU_BACKEND_DAWN || defined IMGUI_IMPL_WEBGPU_BACKEND_WGVK // Dawn renamed WGPUProgrammableStageDescriptor to WGPUComputeState (see: https://github.com/webgpu-native/webgpu-headers/pull/413) // Using type alias until WGPU adopts the same naming convention (#8369) using WGPUProgrammableStageDescriptor = WGPUComputeState; @@ -185,6 +185,60 @@ fn main(in: VertexOutput) -> @location(0) vec4 { } )"; +// Same shader as __shader_vert_wgsl[] but compiled as SPIRV. +// 'wgslc -o vert.spv vert.wgsl' + 'binary_to_compressed_c -u8 -nocompress vert.spv' +static const unsigned char __shader_vert_spirv[1996] = +{ + 3,2,35,7,0,3,1,0,1,0,23,0,90,0,0,0,0,0,0,0,17,0,2,0,1,0,0,0,14,0,3,0,0,0,0,0,1,0,0,0,15,0,12,0,0,0,0,0,78,0,0,0,109,97,105,110,0,0,0,0,8,0,0,0,12,0,0,0,13,0,0,0,16,0,0,0,18,0,0,0,19, + 0,0,0,21,0,0,0,71,0,4,0,4,0,0,0,6,0,0,0,16,0,0,0,72,0,5,0,3,0,0,0,0,0,0,0,35,0,0,0,0,0,0,0,71,0,3,0,3,0,0,0,2,0,0,0,71,0,4,0,1,0,0,0,34,0,0,0,0,0,0,0,71,0,4,0,1,0,0,0,33,0,0,0,0,0, + 0,0,71,0,3,0,1,0,0,0,24,0,0,0,71,0,4,0,8,0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,12,0,0,0,30,0,0,0,1,0,0,0,71,0,4,0,13,0,0,0,30,0,0,0,2,0,0,0,71,0,4,0,16,0,0,0,11,0,0,0,0,0,0,0,71,0,4,0,18, + 0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,19,0,0,0,30,0,0,0,1,0,0,0,71,0,4,0,21,0,0,0,11,0,0,0,1,0,0,0,21,0,4,0,6,0,0,0,32,0,0,0,0,0,0,0,23,0,4,0,5,0,0,0,6,0,0,0,4,0,0,0,43,0,4,0,6,0,0,0,7,0, + 0,0,5,0,0,0,28,0,4,0,4,0,0,0,5,0,0,0,7,0,0,0,30,0,3,0,3,0,0,0,4,0,0,0,32,0,4,0,2,0,0,0,2,0,0,0,3,0,0,0,59,0,4,0,2,0,0,0,1,0,0,0,2,0,0,0,22,0,3,0,11,0,0,0,32,0,0,0,23,0,4,0,10,0,0,0, + 11,0,0,0,2,0,0,0,32,0,4,0,9,0,0,0,1,0,0,0,10,0,0,0,59,0,4,0,9,0,0,0,8,0,0,0,1,0,0,0,59,0,4,0,9,0,0,0,12,0,0,0,1,0,0,0,23,0,4,0,15,0,0,0,11,0,0,0,4,0,0,0,32,0,4,0,14,0,0,0,1,0,0,0,15, + 0,0,0,59,0,4,0,14,0,0,0,13,0,0,0,1,0,0,0,32,0,4,0,17,0,0,0,3,0,0,0,15,0,0,0,59,0,4,0,17,0,0,0,16,0,0,0,3,0,0,0,59,0,4,0,17,0,0,0,18,0,0,0,3,0,0,0,32,0,4,0,20,0,0,0,3,0,0,0,10,0,0,0, + 59,0,4,0,20,0,0,0,19,0,0,0,3,0,0,0,32,0,4,0,22,0,0,0,3,0,0,0,11,0,0,0,59,0,4,0,22,0,0,0,21,0,0,0,3,0,0,0,30,0,5,0,24,0,0,0,15,0,0,0,15,0,0,0,10,0,0,0,30,0,5,0,25,0,0,0,10,0,0,0,10, + 0,0,0,15,0,0,0,33,0,4,0,27,0,0,0,24,0,0,0,25,0,0,0,32,0,4,0,30,0,0,0,7,0,0,0,24,0,0,0,46,0,3,0,24,0,0,0,31,0,0,0,32,0,4,0,33,0,0,0,7,0,0,0,15,0,0,0,43,0,4,0,6,0,0,0,34,0,0,0,0,0,0, + 0,24,0,4,0,36,0,0,0,15,0,0,0,4,0,0,0,43,0,4,0,11,0,0,0,40,0,0,0,0,0,0,0,43,0,4,0,11,0,0,0,41,0,0,0,0,0,128,63,43,0,4,0,6,0,0,0,44,0,0,0,1,0,0,0,32,0,4,0,47,0,0,0,7,0,0,0,10,0,0,0,43, + 0,4,0,6,0,0,0,48,0,0,0,2,0,0,0,33,0,4,0,52,0,0,0,36,0,0,0,6,0,0,0,43,0,4,0,6,0,0,0,55,0,0,0,16,0,0,0,32,0,4,0,57,0,0,0,2,0,0,0,5,0,0,0,43,0,4,0,6,0,0,0,66,0,0,0,32,0,0,0,43,0,4,0,6, + 0,0,0,72,0,0,0,48,0,0,0,19,0,2,0,79,0,0,0,33,0,3,0,80,0,0,0,79,0,0,0,54,0,5,0,24,0,0,0,23,0,0,0,0,0,0,0,27,0,0,0,55,0,3,0,25,0,0,0,26,0,0,0,248,0,2,0,28,0,0,0,59,0,5,0,30,0,0,0,29, + 0,0,0,7,0,0,0,31,0,0,0,65,0,5,0,33,0,0,0,32,0,0,0,29,0,0,0,34,0,0,0,57,0,5,0,36,0,0,0,35,0,0,0,37,0,0,0,34,0,0,0,81,0,5,0,10,0,0,0,38,0,0,0,26,0,0,0,0,0,0,0,80,0,6,0,15,0,0,0,39,0, + 0,0,38,0,0,0,40,0,0,0,41,0,0,0,145,0,5,0,15,0,0,0,42,0,0,0,35,0,0,0,39,0,0,0,62,0,4,0,32,0,0,0,42,0,0,0,0,0,0,0,65,0,5,0,33,0,0,0,43,0,0,0,29,0,0,0,44,0,0,0,81,0,5,0,15,0,0,0,45,0, + 0,0,26,0,0,0,2,0,0,0,62,0,4,0,43,0,0,0,45,0,0,0,0,0,0,0,65,0,5,0,47,0,0,0,46,0,0,0,29,0,0,0,48,0,0,0,81,0,5,0,10,0,0,0,49,0,0,0,26,0,0,0,1,0,0,0,62,0,4,0,46,0,0,0,49,0,0,0,0,0,0,0, + 61,0,5,0,24,0,0,0,50,0,0,0,29,0,0,0,0,0,0,0,254,0,2,0,50,0,0,0,56,0,1,0,54,0,5,0,36,0,0,0,37,0,0,0,0,0,0,0,52,0,0,0,55,0,3,0,6,0,0,0,51,0,0,0,248,0,2,0,53,0,0,0,134,0,5,0,6,0,0,0,54, + 0,0,0,51,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,56,0,0,0,1,0,0,0,34,0,0,0,54,0,0,0,61,0,5,0,5,0,0,0,58,0,0,0,56,0,0,0,0,0,0,0,124,0,4,0,15,0,0,0,59,0,0,0,58,0,0,0,128,0,5,0,6,0,0,0,60,0, + 0,0,55,0,0,0,51,0,0,0,134,0,5,0,6,0,0,0,61,0,0,0,60,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,62,0,0,0,1,0,0,0,34,0,0,0,61,0,0,0,61,0,5,0,5,0,0,0,63,0,0,0,62,0,0,0,0,0,0,0,124,0,4,0,15,0,0, + 0,64,0,0,0,63,0,0,0,128,0,5,0,6,0,0,0,65,0,0,0,66,0,0,0,51,0,0,0,134,0,5,0,6,0,0,0,67,0,0,0,65,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,68,0,0,0,1,0,0,0,34,0,0,0,67,0,0,0,61,0,5,0,5,0,0,0, + 69,0,0,0,68,0,0,0,0,0,0,0,124,0,4,0,15,0,0,0,70,0,0,0,69,0,0,0,128,0,5,0,6,0,0,0,71,0,0,0,72,0,0,0,51,0,0,0,134,0,5,0,6,0,0,0,73,0,0,0,71,0,0,0,55,0,0,0,65,0,6,0,57,0,0,0,74,0,0,0, + 1,0,0,0,34,0,0,0,73,0,0,0,61,0,5,0,5,0,0,0,75,0,0,0,74,0,0,0,0,0,0,0,124,0,4,0,15,0,0,0,76,0,0,0,75,0,0,0,80,0,7,0,36,0,0,0,77,0,0,0,59,0,0,0,64,0,0,0,70,0,0,0,76,0,0,0,254,0,2,0,77, + 0,0,0,56,0,1,0,54,0,5,0,79,0,0,0,78,0,0,0,0,0,0,0,80,0,0,0,248,0,2,0,81,0,0,0,61,0,5,0,10,0,0,0,82,0,0,0,8,0,0,0,0,0,0,0,61,0,5,0,10,0,0,0,83,0,0,0,12,0,0,0,0,0,0,0,61,0,5,0,15,0,0, + 0,84,0,0,0,13,0,0,0,0,0,0,0,80,0,6,0,25,0,0,0,85,0,0,0,82,0,0,0,83,0,0,0,84,0,0,0,57,0,5,0,24,0,0,0,86,0,0,0,23,0,0,0,85,0,0,0,81,0,5,0,15,0,0,0,87,0,0,0,86,0,0,0,0,0,0,0,62,0,4,0, + 16,0,0,0,87,0,0,0,0,0,0,0,81,0,5,0,15,0,0,0,88,0,0,0,86,0,0,0,1,0,0,0,62,0,4,0,18,0,0,0,88,0,0,0,0,0,0,0,81,0,5,0,10,0,0,0,89,0,0,0,86,0,0,0,2,0,0,0,62,0,4,0,19,0,0,0,89,0,0,0,0,0, + 0,0,62,0,4,0,21,0,0,0,41,0,0,0,0,0,0,0,253,0,1,0,56,0,1,0, +}; + +// Same shader as __shader_frag_wgsl[] but compiled as SPIRV. +// 'wgslc -o frag.spv frag.wgsl' + 'binary_to_compressed_c -u8 -nocompress frag.spv' +static const unsigned char __shader_frag_spirv[1392] = +{ + 3,2,35,7,0,3,1,0,1,0,23,0,60,0,0,0,0,0,0,0,17,0,2,0,1,0,0,0,11,0,6,0,48,0,0,0,71,76,83,76,46,115,116,100,46,52,53,48,0,0,0,0,14,0,3,0,0,0,0,0,1,0,0,0,15,0,9,0,4,0,0,0,51,0,0,0,109, + 97,105,110,0,0,0,0,15,0,0,0,18,0,0,0,19,0,0,0,22,0,0,0,16,0,3,0,51,0,0,0,7,0,0,0,71,0,4,0,4,0,0,0,6,0,0,0,16,0,0,0,72,0,5,0,3,0,0,0,0,0,0,0,35,0,0,0,0,0,0,0,71,0,3,0,3,0,0,0,2,0,0, + 0,71,0,4,0,1,0,0,0,34,0,0,0,0,0,0,0,71,0,4,0,1,0,0,0,33,0,0,0,0,0,0,0,71,0,3,0,1,0,0,0,24,0,0,0,71,0,4,0,8,0,0,0,34,0,0,0,0,0,0,0,71,0,4,0,8,0,0,0,33,0,0,0,1,0,0,0,71,0,4,0,11,0,0, + 0,34,0,0,0,1,0,0,0,71,0,4,0,11,0,0,0,33,0,0,0,0,0,0,0,71,0,4,0,15,0,0,0,11,0,0,0,15,0,0,0,71,0,4,0,18,0,0,0,30,0,0,0,0,0,0,0,71,0,4,0,19,0,0,0,30,0,0,0,1,0,0,0,71,0,4,0,22,0,0,0,30, + 0,0,0,0,0,0,0,21,0,4,0,6,0,0,0,32,0,0,0,0,0,0,0,23,0,4,0,5,0,0,0,6,0,0,0,4,0,0,0,43,0,4,0,6,0,0,0,7,0,0,0,5,0,0,0,28,0,4,0,4,0,0,0,5,0,0,0,7,0,0,0,30,0,3,0,3,0,0,0,4,0,0,0,32,0,4,0, + 2,0,0,0,2,0,0,0,3,0,0,0,59,0,4,0,2,0,0,0,1,0,0,0,2,0,0,0,26,0,2,0,10,0,0,0,32,0,4,0,9,0,0,0,0,0,0,0,10,0,0,0,59,0,4,0,9,0,0,0,8,0,0,0,0,0,0,0,22,0,3,0,14,0,0,0,32,0,0,0,25,0,9,0,13, + 0,0,0,14,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,32,0,4,0,12,0,0,0,0,0,0,0,13,0,0,0,59,0,4,0,12,0,0,0,11,0,0,0,0,0,0,0,23,0,4,0,17,0,0,0,14,0,0,0,4,0,0,0,32,0,4,0,16, + 0,0,0,1,0,0,0,17,0,0,0,59,0,4,0,16,0,0,0,15,0,0,0,1,0,0,0,59,0,4,0,16,0,0,0,18,0,0,0,1,0,0,0,23,0,4,0,21,0,0,0,14,0,0,0,2,0,0,0,32,0,4,0,20,0,0,0,1,0,0,0,21,0,0,0,59,0,4,0,20,0,0,0, + 19,0,0,0,1,0,0,0,32,0,4,0,23,0,0,0,3,0,0,0,17,0,0,0,59,0,4,0,23,0,0,0,22,0,0,0,3,0,0,0,30,0,5,0,25,0,0,0,17,0,0,0,17,0,0,0,21,0,0,0,33,0,4,0,27,0,0,0,17,0,0,0,25,0,0,0,27,0,3,0,34, + 0,0,0,13,0,0,0,23,0,4,0,38,0,0,0,14,0,0,0,3,0,0,0,32,0,4,0,40,0,0,0,2,0,0,0,5,0,0,0,43,0,4,0,6,0,0,0,41,0,0,0,0,0,0,0,43,0,4,0,6,0,0,0,42,0,0,0,4,0,0,0,19,0,2,0,52,0,0,0,33,0,3,0,53, + 0,0,0,52,0,0,0,54,0,5,0,17,0,0,0,24,0,0,0,0,0,0,0,27,0,0,0,55,0,3,0,25,0,0,0,26,0,0,0,248,0,2,0,28,0,0,0,81,0,5,0,17,0,0,0,29,0,0,0,26,0,0,0,1,0,0,0,61,0,5,0,13,0,0,0,30,0,0,0,11,0, + 0,0,0,0,0,0,61,0,5,0,10,0,0,0,31,0,0,0,8,0,0,0,0,0,0,0,81,0,5,0,21,0,0,0,32,0,0,0,26,0,0,0,2,0,0,0,86,0,5,0,34,0,0,0,33,0,0,0,30,0,0,0,31,0,0,0,87,0,6,0,17,0,0,0,35,0,0,0,33,0,0,0, + 32,0,0,0,0,0,0,0,133,0,5,0,17,0,0,0,36,0,0,0,29,0,0,0,35,0,0,0,79,0,8,0,38,0,0,0,37,0,0,0,36,0,0,0,36,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,65,0,6,0,40,0,0,0,39,0,0,0,1,0,0,0,41,0,0,0,42,0, + 0,0,61,0,5,0,5,0,0,0,43,0,0,0,39,0,0,0,0,0,0,0,81,0,5,0,6,0,0,0,44,0,0,0,43,0,0,0,0,0,0,0,124,0,4,0,14,0,0,0,45,0,0,0,44,0,0,0,80,0,6,0,38,0,0,0,46,0,0,0,45,0,0,0,45,0,0,0,45,0,0,0, + 12,0,7,0,38,0,0,0,47,0,0,0,48,0,0,0,26,0,0,0,37,0,0,0,46,0,0,0,81,0,5,0,14,0,0,0,49,0,0,0,36,0,0,0,3,0,0,0,80,0,5,0,17,0,0,0,50,0,0,0,47,0,0,0,49,0,0,0,254,0,2,0,50,0,0,0,56,0,1,0, + 54,0,5,0,52,0,0,0,51,0,0,0,0,0,0,0,53,0,0,0,248,0,2,0,54,0,0,0,61,0,5,0,17,0,0,0,55,0,0,0,15,0,0,0,0,0,0,0,61,0,5,0,17,0,0,0,56,0,0,0,18,0,0,0,0,0,0,0,61,0,5,0,21,0,0,0,57,0,0,0,19, + 0,0,0,0,0,0,0,80,0,6,0,25,0,0,0,58,0,0,0,55,0,0,0,56,0,0,0,57,0,0,0,57,0,5,0,17,0,0,0,59,0,0,0,24,0,0,0,58,0,0,0,62,0,4,0,22,0,0,0,59,0,0,0,0,0,0,0,253,0,1,0,56,0,1,0, +}; + static void SafeRelease(ImDrawIdx*& res) { if (res) @@ -273,6 +327,25 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c return stage_desc; } +static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const void* spirv_binary, size_t spirv_length) +{ + ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); + + WGPUShaderSourceSPIRV spirv_desc = {}; + spirv_desc.chain.sType = WGPUSType_ShaderSourceSPIRV; + spirv_desc.code = (const uint32_t *)spirv_binary; + spirv_desc.codeSize = ((uint32_t)(spirv_length / 4)); + + WGPUShaderModuleDescriptor desc = {}; + desc.nextInChain = (WGPUChainedStruct*)&spirv_desc; + + WGPUProgrammableStageDescriptor stage_desc = {}; + stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); + + stage_desc.entryPoint = { "main", WGPU_STRLEN }; + return stage_desc; +} + static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); @@ -666,13 +739,14 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() // Create the vertex shader WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl); + if (!vertex_shader_desc.module) vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_spirv, sizeof(__shader_vert_spirv)); graphics_pipeline_desc.vertex.module = vertex_shader_desc.module; graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint; // Vertex input configuration WGPUVertexAttribute attribute_desc[] = { -#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN +#if defined IMGUI_IMPL_WEBGPU_BACKEND_DAWN || defined IMGUI_IMPL_WEBGPU_BACKEND_WGVK { nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 }, { nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 }, { nullptr, WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 }, @@ -694,6 +768,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() // Create the pixel shader WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl); + if (!pixel_shader_desc.module) pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_spirv, sizeof(__shader_frag_spirv)); // Create the blending setup WGPUBlendState blend_state = {}; @@ -806,6 +881,8 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) #endif #elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) io.BackendRendererName = "imgui_impl_wgpu (WGPU, Native)"; +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) + io.BackendRendererName = "imgui_impl_wgpu (WGVK, Native)"; #endif io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. @@ -877,7 +954,7 @@ void ImGui_ImplWGPU_NewFrame() bool ImGui_ImplWGPU_IsSurfaceStatusError(WGPUSurfaceGetCurrentTextureStatus status) { -#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) return (status == WGPUSurfaceGetCurrentTextureStatus_Error); #else return (status == WGPUSurfaceGetCurrentTextureStatus_OutOfMemory || status == WGPUSurfaceGetCurrentTextureStatus_DeviceLost); @@ -894,7 +971,7 @@ bool ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(WGPUSurfaceGetCurrentTextureStatus } // Helpers to obtain a string -#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) const char* ImGui_ImplWGPU_GetErrorTypeName(WGPUErrorType type) { switch (type) diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 346538a4..c06c25f8 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -3,7 +3,7 @@ // (Please note that WebGPU is a recent API, may not be supported by all browser, and its ecosystem is generally a mess) // When targeting native platforms: -// - One of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU *must* be provided. +// - One of IMGUI_IMPL_WEBGPU_BACKEND_DAWN, IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_WGVK *must* be provided. // When targeting Emscripten: // - We now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN and requires Emscripten 4.0.10+, which correspond to using Emscripten '--use-port=emdawnwebgpu'. // - Emscripten < 4.0.10 is not supported anymore (old '-sUSE_WEBGPU=1' option). @@ -12,6 +12,7 @@ // This requirement may be removed once WebGPU stabilizes and backends converge on a unified interface. //#define IMGUI_IMPL_WEBGPU_BACKEND_DAWN //#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU +//#define IMGUI_IMPL_WEBGPU_BACKEND_WGVK // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef! diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index cae04ac4..9d48258b 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -340,7 +340,7 @@ static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request"); return acquired_device.MoveToCHandle(); } -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) { IM_UNUSED(userdata2); @@ -441,17 +441,19 @@ bool InitWGPU(GLFWwindow* window) preferred_fmt = surface_capabilities.formats[0]; // WGPU backend: Adapter and Device acquisition, Surface creation -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) WGPUInstanceDescriptor instanceDesc = {}; WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny; instanceDesc.requiredFeatureCount = 1; instanceDesc.requiredFeatures = &timedWaitAny; wgpu_instance = wgpuCreateInstance(&instanceDesc); +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) wgpuSetLogCallback( [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr ); wgpuSetLogLevel(WGPULogLevel_Warn); +#endif WGPUAdapter adapter = RequestAdapter(wgpu_instance); ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter); diff --git a/examples/example_sdl2_wgpu/main.cpp b/examples/example_sdl2_wgpu/main.cpp index 85f8b39e..6898d03c 100644 --- a/examples/example_sdl2_wgpu/main.cpp +++ b/examples/example_sdl2_wgpu/main.cpp @@ -326,7 +326,7 @@ static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request"); return acquired_device.MoveToCHandle(); } -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) { IM_UNUSED(userdata2); @@ -428,17 +428,19 @@ static bool InitWGPU(SDL_Window* window) preferred_fmt = surface_capabilities.formats[0]; // WGPU backend: Adapter and Device acquisition, Surface creation -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) WGPUInstanceDescriptor instanceDesc = {}; WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny; instanceDesc.requiredFeatureCount = 1; instanceDesc.requiredFeatures = &timedWaitAny; wgpu_instance = wgpuCreateInstance(&instanceDesc); +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) wgpuSetLogCallback( [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr ); wgpuSetLogLevel(WGPULogLevel_Warn); +#endif WGPUAdapter adapter = RequestAdapter(wgpu_instance); ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter); diff --git a/examples/example_sdl3_wgpu/main.cpp b/examples/example_sdl3_wgpu/main.cpp index 939cdfca..4aee4ef6 100644 --- a/examples/example_sdl3_wgpu/main.cpp +++ b/examples/example_sdl3_wgpu/main.cpp @@ -337,7 +337,7 @@ static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request"); return acquired_device.MoveToCHandle(); } -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2) { IM_UNUSED(userdata2); @@ -439,17 +439,19 @@ static bool InitWGPU(SDL_Window* window) preferred_fmt = surface_capabilities.formats[0]; // WGPU backend: Adapter and Device acquisition, Surface creation -#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) +#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) WGPUInstanceDescriptor instanceDesc = {}; WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny; instanceDesc.requiredFeatureCount = 1; instanceDesc.requiredFeatures = &timedWaitAny; wgpu_instance = wgpuCreateInstance(&instanceDesc); +#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) wgpuSetLogCallback( [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr ); wgpuSetLogLevel(WGPULogLevel_Warn); +#endif WGPUAdapter adapter = RequestAdapter(wgpu_instance); ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter); From 59183cf782bd3dd086ac0bad3aac5a1ab2999f53 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Mar 2026 16:26:45 +0100 Subject: [PATCH 33/37] Backends, Examples: WebGPU: added support for WGVK. Remaining amends. (#9316, #9246, #9257) --- backends/imgui_impl_wgpu.cpp | 13 +++++++------ docs/CHANGELOG.txt | 4 ++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index c7458e46..86bb36cc 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-03-25: Added support for WGVK native backend via IMGUI_IMPL_WEBGPU_BACKEND_WGVK define, with SPIRV shaders if WGSL is not available. (#9316, #9246, #9257) // 2026-03-09: Removed support for Emscripten < 4.0.10. (#9281) // 2025-10-16: Update to compile with Dawn and Emscripten's 4.0.10+ '--use-port=emdawnwebgpu' ports. (#8381, #8898) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. @@ -309,7 +310,7 @@ static void SafeRelease(FrameResources& res) SafeRelease(res.VertexBufferHost); } -static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source) +static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleWGSL(const char* wgsl_source) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); @@ -327,7 +328,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c return stage_desc; } -static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const void* spirv_binary, size_t spirv_length) +static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleSPIRV(const void* spirv_binary, size_t spirv_length) { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); @@ -738,8 +739,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() graphics_pipeline_desc.layout = wgpuDeviceCreatePipelineLayout(bd->wgpuDevice, &layout_desc); // Create the vertex shader - WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl); - if (!vertex_shader_desc.module) vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_spirv, sizeof(__shader_vert_spirv)); + WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModuleWGSL(__shader_vert_wgsl); + if (!vertex_shader_desc.module) vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModuleSPIRV(__shader_vert_spirv, sizeof(__shader_vert_spirv)); graphics_pipeline_desc.vertex.module = vertex_shader_desc.module; graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint; @@ -767,8 +768,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() graphics_pipeline_desc.vertex.buffers = buffer_layouts; // Create the pixel shader - WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl); - if (!pixel_shader_desc.module) pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_spirv, sizeof(__shader_frag_spirv)); + WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModuleWGSL(__shader_frag_wgsl); + if (!pixel_shader_desc.module) pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModuleSPIRV(__shader_frag_spirv, sizeof(__shader_frag_spirv)); // Create the blending setup WGPUBlendState blend_state = {}; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e1ddbad9..948a17da 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -152,6 +152,10 @@ Other Changes: vertex/index buffers. (#9262) [@jaenis] - WebGPU: fixed version check for Emscripten 5.0.0+. - WebGPU: removed support for Emscripten <4.0.10. (#9281) [@ypujante] + - WebGPU: added support for WGVK native backend via IMGUI_IMPL_WEBGPU_BACKEND_WGVK, + using SPIRV shaders if WGSL is not available. (#9316, #9246, #9257) [@r-lyeh] + (WGVK is a lightweight alternative to Dawn or WGPU for native applications, + which is easier to build/setup, see: https://github.com/manuel5975p/WGVK) - Examples: - Emscripten: added `tabindex=-1` to canvas in our shell_minimal.htm. Without it, the canvas was not focusable in the DOM, which in turn make some backends From ed2e5dd0f028e836e514c46a3d67b95498bcd5f1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 25 Mar 2026 16:16:15 +0100 Subject: [PATCH 34/37] Examples+WebGPU: added support for WGVK in cmakefiles. (#9316, #9246, #9257) --- docs/CHANGELOG.txt | 3 +- examples/example_glfw_wgpu/CMakeLists.txt | 51 ++++++++++++++++------- examples/example_sdl2_wgpu/CMakeLists.txt | 49 +++++++++++++++------- examples/example_sdl3_wgpu/CMakeLists.txt | 49 +++++++++++++++------- 4 files changed, 108 insertions(+), 44 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 948a17da..2b3ee596 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -170,7 +170,8 @@ Other Changes: - WebGPU: fixed undefined behaviors in example code for requesting adapter and device. (#9246, #9256) [@r-lyeh] - SDL2+WebGPU: fixed hi-dpi handling. (#9300) [@ypujante] - - GLFW/SDL2/SDL3+WebGPU: removed suport for Emscripten <4.0.10. (#9281) [@ypujante] + - GLFW/SDL2/SDL3+WebGPU: added support for WGVK. (#9316, #9246, #9257) [@r-lyeh, @ocornut] + - GLFW/SDL2/SDL3+WebGPU: removed support for Emscripten <4.0.10. (#9281) [@ypujante] ----------------------------------------------------------------------- diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt index f1951cb5..53bcda33 100644 --- a/examples/example_glfw_wgpu/CMakeLists.txt +++ b/examples/example_glfw_wgpu/CMakeLists.txt @@ -1,19 +1,24 @@ -# Building for desktop (WebGPU-native) with Dawn: +# Building for desktop with Dawn: # 1. git clone https://github.com/google/dawn dawn # 2. cmake -B build -DIMGUI_DAWN_DIR=dawn # 3. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_glfw_wgpu[.exe] -# * build/example_glfw_wgpu[.exe] +# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe] -# Building for desktop (WGPU-Native) with WGPU-Native: +# Building for desktop with WGPU-Native: # 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases # 2. unzip the downloaded file in your_preferred_folder # 3. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory) # 4. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_glfw_wgpu[.exe] -# * build/example_glfw_wgpu[.exe] +# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe] + +# Building for desktop with WGVK (MUCH EASIER) +# 1. git clone https://github.com/manuel5975p/WGVK dawn +# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_glfw_wgpu[.exe] or build/Debug/example_glfw_wgpu[.exe] # Building for Emscripten: # 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html @@ -66,13 +71,14 @@ if(EMSCRIPTEN) set(LIBRARIES glfw) add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) else() # Native/Desktop build - # Check DAWN/WGPU directory - if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR) # if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified - message(FATAL_ERROR "Please specify the Dawn or WGPU base directory") - endif() - if(IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR) # both IMGUI_DAWN_DIR and IMGUI_WGPU_DIR cannot be set - message(FATAL_ERROR "Please specify only one between Dawn / WGPU base directory") + # Check DAWN/WGPU/WGVK directory + # If it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR or IMGUI_WGVK_DIR must be specified + if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR AND NOT IMGUI_WGVK_DIR) + message(FATAL_ERROR "Please specify one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + if((IMGUI_DAWN_DIR AND (IMGUI_WGPU_DIR OR IMGUI_WGVK_DIR)) OR (IMGUI_WGPU_DIR AND IMGUI_WGVK_DIR)) + message(FATAL_ERROR "Please specify only one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") endif() if(APPLE) # Add SDL2 module to get Surface, with libs and file property for MacOS build @@ -82,7 +88,7 @@ else() # Native/Desktop build find_package(glfw3 REQUIRED) - if(IMGUI_DAWN_DIR) # DAWN-Native build options + if(IMGUI_DAWN_DIR) list(APPEND CMAKE_PREFIX_PATH ${IMGUI_DAWN_DIR}) find_package(Dawn) # Search for a Dawn installation using IMGUI_DAWN_DIR in CMAKE_PREFIX_PATH if(Dawn_FOUND) @@ -125,7 +131,9 @@ else() # Native/Desktop build set(LIBRARIES webgpu_dawn glfw) endif() - else() # WGPU-Native build options + endif() + + if(IMGUI_WGPU_DIR) set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib) find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED) if(WIN32) @@ -136,6 +144,9 @@ else() # Native/Desktop build set(LIBRARIES glfw ${WGPU_LIBRARY} ${OS_LIBRARIES}) endif() + + if(IMGUI_WGVK_DIR) + endif() endif() add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES}) @@ -168,10 +179,20 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(NOT Dawn_FOUND) target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp) endif() - else() + endif() + if(IMGUI_WGPU_DIR) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU") target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include) endif() + if(IMGUI_WGVK_DIR) + target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) + if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) + endif() + endif() + target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES}) else() # Emscripten settings diff --git a/examples/example_sdl2_wgpu/CMakeLists.txt b/examples/example_sdl2_wgpu/CMakeLists.txt index c2c45094..3bed79fd 100644 --- a/examples/example_sdl2_wgpu/CMakeLists.txt +++ b/examples/example_sdl2_wgpu/CMakeLists.txt @@ -1,19 +1,24 @@ -# Building for desktop (WebGPU-native) with Dawn: +# Building for desktop with Dawn: # 1. git clone https://github.com/google/dawn dawn # 2. cmake -B build -DIMGUI_DAWN_DIR=dawn # 3. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_sdl2_wgpu[.exe] -# * build/example_sdl2_wgpu[.exe] +# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe] -# Building for desktop (WGPU-Native) with WGPU-Native: +# Building for desktop with WGPU-Native: # 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases # 2. unzip the downloaded file in your_preferred_folder # 3. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory) # 4. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_sdl2_wgpu[.exe] -# * build/example_sdl2_wgpu[.exe] +# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe] + +# Building for desktop with WGVK (MUCH EASIER) +# 1. git clone https://github.com/manuel5975p/WGVK dawn +# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl2_wgpu[.exe] or build/Debug/example_sdl2_wgpu[.exe] # Building for Emscripten: # 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html @@ -60,12 +65,14 @@ if(EMSCRIPTEN) add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) else() # Native/Desktop build - if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR) # if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified - message(FATAL_ERROR "Please specify the Dawn or WGPU base directory") - endif() - if(IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR) # both IMGUI_DAWN_DIR and IMGUI_WGPU_DIR cannot be set - message(FATAL_ERROR "Please specify only one between Dawn / WGPU base directory") + # Check DAWN/WGPU/WGVK directory + # If it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR or IMGUI_WGVK_DIR must be specified + if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR AND NOT IMGUI_WGVK_DIR) + message(FATAL_ERROR "Please specify one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + if((IMGUI_DAWN_DIR AND (IMGUI_WGPU_DIR OR IMGUI_WGVK_DIR)) OR (IMGUI_WGPU_DIR AND IMGUI_WGVK_DIR)) + message(FATAL_ERROR "Please specify only one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") endif() if(APPLE) # Add SDL2 module to get Surface, with libs and file property for MacOS build @@ -75,7 +82,7 @@ else() # Native/Desktop build find_package(SDL2 REQUIRED) # SDL_MAIN_HANDLED - if(IMGUI_DAWN_DIR) # DAWN-Native build options + if(IMGUI_DAWN_DIR) list(APPEND CMAKE_PREFIX_PATH ${IMGUI_DAWN_DIR}) find_package(Threads) # required from Dawn installation find_package(Dawn) # Search for a Dawn installation using IMGUI_DAWN_DIR in CMAKE_PREFIX_PATH @@ -119,8 +126,9 @@ else() # Native/Desktop build set(LIBRARIES webgpu_dawn ${OS_LIBRARIES}) endif() - else() # WGPU-Native build options + endif() + if(IMGUI_WGPU_DIR) set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib) find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED) if(WIN32) @@ -131,6 +139,10 @@ else() # Native/Desktop build set(LIBRARIES ${WGPU_LIBRARY} ${OS_LIBRARIES}) endif() + + if(IMGUI_WGVK_DIR) + endif() + endif() add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES}) @@ -163,10 +175,19 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(NOT Dawn_FOUND) target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp) endif() - else() + endif() + if(IMGUI_WGPU_DIR) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU") target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include) endif() + if(IMGUI_WGVK_DIR) + target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) + if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) + endif() + endif() target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES} ${SDL2_LIBRARIES}) else() # Emscripten settings diff --git a/examples/example_sdl3_wgpu/CMakeLists.txt b/examples/example_sdl3_wgpu/CMakeLists.txt index 6e418821..85de861e 100644 --- a/examples/example_sdl3_wgpu/CMakeLists.txt +++ b/examples/example_sdl3_wgpu/CMakeLists.txt @@ -1,19 +1,24 @@ -# Building for desktop (WebGPU-native) with Dawn: +# Building for desktop with Dawn: # 1. git clone https://github.com/google/dawn dawn # 2. cmake -B build -DIMGUI_DAWN_DIR=dawn # 3. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_sdl3_wgpu[.exe] -# * build/example_sdl3_wgpu[.exe] +# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe] -# Building for desktop (WGPU-Native) with WGPU-Native: +# Building for desktop with WGPU-Native: # 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases # 2. unzip the downloaded file in your_preferred_folder # 3. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory) # 4. cmake --build build # The resulting binary will be found at one of the following locations: -# * build/Debug/example_sdl3_wgpu[.exe] -# * build/example_sdl3_wgpu[.exe] +# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe] + +# Building for desktop with WGVK (MUCH EASIER) +# 1. git clone https://github.com/manuel5975p/WGVK dawn +# 2. cmake -B build -DIMGUI_WGVK_DIR=wgvk +# 3. cmake --build build +# The resulting binary will be found at one of the following locations: +# * build/example_sdl3_wgpu[.exe] or build/Debug/example_sdl3_wgpu[.exe] # Building for Emscripten: # 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html @@ -60,12 +65,14 @@ if(EMSCRIPTEN) add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) else() # Native/Desktop build - if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR) # if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified - message(FATAL_ERROR "Please specify the Dawn or WGPU base directory") - endif() - if(IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR) # both IMGUI_DAWN_DIR and IMGUI_WGPU_DIR cannot be set - message(FATAL_ERROR "Please specify only one between Dawn / WGPU base directory") + # Check DAWN/WGPU/WGVK directory + # If it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR or IMGUI_WGVK_DIR must be specified + if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR AND NOT IMGUI_WGVK_DIR) + message(FATAL_ERROR "Please specify one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") + endif() + if((IMGUI_DAWN_DIR AND (IMGUI_WGPU_DIR OR IMGUI_WGVK_DIR)) OR (IMGUI_WGPU_DIR AND IMGUI_WGVK_DIR)) + message(FATAL_ERROR "Please specify only one of IMGUI_DAWN_DIR/IMGUI_WGPU_DIR/IMGUI_WGVK_DIR base directory.") endif() if(APPLE) # Add SDL3 module to get Surface, with libs and file property for MacOS build @@ -75,7 +82,7 @@ else() # Native/Desktop build find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3) - if(IMGUI_DAWN_DIR) # DAWN-Native build options + if(IMGUI_DAWN_DIR) list(APPEND CMAKE_PREFIX_PATH ${IMGUI_DAWN_DIR}) find_package(Threads) # required from Dawn installation find_package(Dawn) # Search for a Dawn installation using IMGUI_DAWN_DIR in CMAKE_PREFIX_PATH @@ -119,7 +126,9 @@ else() # Native/Desktop build set(LIBRARIES webgpu_dawn ${OS_LIBRARIES}) endif() - else() # WGPU-Native build options + endif() + + if(IMGUI_WGPU_DIR) set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib) find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED) if(WIN32) @@ -130,6 +139,9 @@ else() # Native/Desktop build set(LIBRARIES ${WGPU_LIBRARY} ${OS_LIBRARIES}) endif() + + if(IMGUI_WGVK_DIR) + endif() endif() add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES}) @@ -162,10 +174,19 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(NOT Dawn_FOUND) target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp) endif() - else() + endif() + if(IMGUI_WGPU_DIR) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU") target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include) endif() + if(IMGUI_WGVK_DIR) + target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) + target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) + if (MSVC) + target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) + endif() + endif() target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES} SDL3::SDL3) else() # Emscripten settings From e9eb04ea95edd0adc98fbf604addccee6ccad178 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 25 Mar 2026 17:44:55 +0100 Subject: [PATCH 35/37] Internals: TempInputText: added callback/user_data parameters and made end of signature match InputText(). (#2718) --- imgui_internal.h | 2 +- imgui_widgets.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index cbdfc95b..1fb90575 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3688,7 +3688,7 @@ namespace ImGui // InputText IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API void InputTextDeactivateHook(ImGuiID id); - IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); + IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return g.ActiveId == id && g.TempInputId == id; } inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c9edbe7c..baec8334 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3696,7 +3696,7 @@ int ImParseFormatPrecision(const char* fmt, int default_precision) // Create text input in place of another active widget (e.g. used when doing a Ctrl+Click on drag/slider widgets) // - This must be submitted right after the item it is overlaying. // FIXME: Facilitate using this in variety of other situations. -bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags) +bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id. // We clear ActiveID on the first frame to allow the InputText() taking it back. @@ -3710,7 +3710,7 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* ImVec2 backup_pos = window->DC.CursorPos; window->DC.CursorPos = bb.Min; g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId; // Using ImGuiInputTextFlags_MergedItem above will skip ItemAdd() so we poke here. - bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_TempInput | ImGuiInputTextFlags_AutoSelectAll); + bool value_changed = InputTextEx(label, NULL, buf, (int)buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_TempInput | ImGuiInputTextFlags_AutoSelectAll, callback, user_data); KeepAliveID(id); // Not done because of ImGuiInputTextFlags_TempInput if (init) { From b62cf3894bc971a1aa8a8bb4006bd502491a06a0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 25 Mar 2026 22:00:14 +0100 Subject: [PATCH 36/37] Inputs: fixed an issue using SetKeyOwner() with ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease on ImGuiMod values. (#9323) --- imgui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a10d0a0c..353eda54 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10078,11 +10078,12 @@ static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value) // [Internal] Do not use directly static ImGuiKeyChord GetMergedModsFromKeys() { + // Bypass IsKeyDown() for the unlikely case where user used a ImGuiInputFlags_LockXXXX on those. ImGuiKeyChord mods = 0; - if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; } - if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; } - if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; } - if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; } + if (ImGui::GetKeyData(ImGuiMod_Ctrl)->Down) { mods |= ImGuiMod_Ctrl; } + if (ImGui::GetKeyData(ImGuiMod_Shift)->Down) { mods |= ImGuiMod_Shift; } + if (ImGui::GetKeyData(ImGuiMod_Alt)->Down) { mods |= ImGuiMod_Alt; } + if (ImGui::GetKeyData(ImGuiMod_Super)->Down) { mods |= ImGuiMod_Super; } return mods; } From bd3c925680d95c1ce82e488a6ec464ecc2698545 Mon Sep 17 00:00:00 2001 From: thedmd Date: Sun, 29 Mar 2026 10:20:24 +0200 Subject: [PATCH 37/37] DrawList: PathArcTo(): fixed erroneous segment count for small arcs on large circles. (#9331, #9313) --- docs/CHANGELOG.txt | 3 +++ imgui_draw.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2b3ee596..60b1806b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -130,6 +130,9 @@ Other Changes: as a convenience for when using e.g. InvisibleButton(). - 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) +- DrawList: + - PathArcTo(): fixed erroneous segment count for pathologically small arcs on large + circles. (#9331, #9313) [@thedmd, @epajarre] - Memory: - Discard/GC of ImDrawList buffers for unused windows favor restoring them to ~Size*1.05 instead of Capacity when awakening again. Facilitate releasing ImDrawList diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 3a15fcd7..680d6af0 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1312,7 +1312,7 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa { const float arc_length = ImAbs(a_max - a_min); const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); - const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); + const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), 1); _PathArcToN(center, radius, a_min, a_max, arc_segment_count); } }