From e0a2f6dea98f76a793a6e62a2bdb7ee9b29f5add Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 10 Sep 2026 18:32:44 +0200 Subject: [PATCH 01/25] Drag and Drop: minor refactor into a helper RenderDragDropTargetRectForViewport() function. --- imgui.cpp | 19 ++++++++++++------- imgui_internal.h | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 105b6c2cf..90a80a3f8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -15357,15 +15357,9 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame) const bool draw_target_rect = payload.Preview && !(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); if (draw_target_rect && g.DragDropTargetFullViewport != 0) - { - ImRect bb = g.DragDropTargetRect; - bb.Expand(-3.5f); - RenderDragDropTargetRectEx(GetForegroundDrawList(), bb, g.Style.DragDropTargetRounding); - } + RenderDragDropTargetRectForViewport(g.DragDropTargetFullViewport, g.DragDropTargetRect); else if (draw_target_rect) - { RenderDragDropTargetRectForItem(r); - } g.DragDropAcceptFrameCount = g.FrameCount; if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1) @@ -15396,6 +15390,17 @@ void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb) window->DrawList->PopClipRect(); } +void ImGui::RenderDragDropTargetRectForViewport(ImGuiID viewport_id, const ImRect& bb) +{ + IM_ASSERT(viewport_id != 0); + IM_UNUSED(viewport_id); // Unused in this branch + ImGuiContext& g = *GImGui; + ImGuiViewport* viewport = g.Viewports[0]; + ImRect bb_padded = bb; + bb_padded.Expand(-g.Style.DragDropTargetPadding); + RenderDragDropTargetRectEx(GetForegroundDrawList(viewport), bb_padded, g.Style.DragDropTargetRounding); +} + void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, float rounding) { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index 924da068d..bac5b3355 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3354,7 +3354,7 @@ namespace ImGui // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. IMGUI_API ImGuiIO& GetIO(ImGuiContext* ctx); IMGUI_API ImGuiPlatformIO& GetPlatformIO(ImGuiContext* ctx); - inline float GetScale() { ImGuiContext& g = *GImGui; return g.Style._MainScale; } // FIXME-DPI: I don't want to formalize this just yet. Because reasons. Please don't use. + inline float GetScale() { ImGuiContext& g = *GImGui; return g.Style._MainScale; } // FIXME-DPI: PLEASE DO NOT USE. I don't want to formalize this just yet. Because reasons. inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); @@ -3674,6 +3674,7 @@ namespace ImGui IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); IMGUI_API void RenderDragDropTargetRectForItem(const ImRect& bb); + IMGUI_API void RenderDragDropTargetRectForViewport(ImGuiID viewport_id, const ImRect& bb); IMGUI_API void RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb, float rounding); // Typing-Select API From ea60e7b7488b473c61d0cdc47b7fc83dd9e7ee4e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 14 Sep 2026 15:56:16 +0200 Subject: [PATCH 02/25] TabBar: added TabBarGetTabPos() helper. # Conflicts: # imgui_internal.h # imgui_widgets.cpp --- imgui_internal.h | 1 + imgui_widgets.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index bac5b3355..a248fc800 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3707,6 +3707,7 @@ namespace ImGui IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar); inline int TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); } IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API ImVec2 TabBarGetTabPos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8a1637f44..62b682adb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -10402,6 +10402,14 @@ const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) return tab_bar->TabsNames.Buf.Data + tab->NameOffset; } +ImVec2 ImGui::TabBarGetTabPos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) +{ + if ((tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0) + return tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f); + else + return tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f); +} + // The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) { @@ -10855,10 +10863,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // Layout const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; size.x = tab->Width; - if (is_central_section) - window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f); - else - window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f); + window->DC.CursorPos = TabBarGetTabPos(tab_bar, tab); ImVec2 pos = window->DC.CursorPos; ImRect bb(pos, pos + size); From c8b9f0d6be440f6588e96ceaa1bc4dcbb4ed1861 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 14 Sep 2026 19:04:23 +0200 Subject: [PATCH 03/25] TabBar: Improved scroll target when clicking a Tab that is larger than the scrolling region width. The margin that is useful to indicate other tabs being available gets in the way of visibility in this case. --- docs/CHANGELOG.txt | 3 +++ imgui_demo.cpp | 2 +- imgui_widgets.cpp | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a460b728d..d68f0c54b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -60,6 +60,9 @@ Other Changes: - TreeNode: - Fixed issues/asserts with 32+ deep trees when using ImGuiTreeNodeFlags_DrawLinesXXX features or other features requiring stack storage. (#9509) [@lasrod] +- TabBar: + - Improved scroll target when clicking a Tab that is larger than the scrolling + region width. - Fonts: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7e0283bc2..ea7a22741 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3748,7 +3748,7 @@ static void DemoWindowWidgetsTabs() ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button); // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs - static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink; + static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyMixed; EditTabBarFittingPolicyFlags(&tab_bar_flags); if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 62b682adb..8d2714772 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -10460,8 +10460,10 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui if (tab->Flags & ImGuiTabItemFlags_SectionMask_) return; + // When scrolling to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) + // Disable the margin if the scrolling section is too small for the target tab: prefer displaying a maximum of the label. ImGuiContext& g = *GImGui; - float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar) + float margin = ImClamp(tab_bar->ScrollingRectMaxX - tab_bar->ScrollingRectMinX - tab->Width, g.Style.ItemInnerSpacing.x, g.FontSize * 1.0f); int order = TabBarGetTabOrder(tab_bar, tab); // Scrolling happens only in the central section (leading/trailing sections are not scrolling) From b00dd14a347c7c5ca59a1e4dd50346d67a29cb0a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 14 Sep 2026 19:25:33 +0200 Subject: [PATCH 04/25] TabBar: using <> scrolling buttons while the selected Tab is at either end of the section mid-section but not fully visible always makes it fully visible. --- docs/CHANGELOG.txt | 5 ++++- imgui_demo.cpp | 11 +++++++++++ imgui_widgets.cpp | 14 +++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d68f0c54b..96d6cd99b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,7 +62,10 @@ Other Changes: features or other features requiring stack storage. (#9509) [@lasrod] - TabBar: - Improved scroll target when clicking a Tab that is larger than the scrolling - region width. + section width. + - Using <> scrolling buttons while the selected Tab is at either end of the + section mid-section but not fully visible always makes it fully visible + (before eventually selecting a Tab in the leading/trailing section) - Fonts: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ea7a22741..7478a99ee 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3744,8 +3744,10 @@ static void DemoWindowWidgetsTabs() // but they tend to make more sense together) static bool show_leading_button = true; static bool show_trailing_button = true; + static bool show_leading_trailing_tabs = false; ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button); ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button); + ImGui::Checkbox("Show Leading+Trailing TabItem()", &show_leading_trailing_tabs); // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyMixed; @@ -3763,6 +3765,15 @@ static void DemoWindowWidgetsTabs() ImGui::EndPopup(); } + // Demo Leading/Trailing Tabs + if (show_leading_trailing_tabs) + { + if (ImGui::BeginTabItem("Leading", NULL, ImGuiTabItemFlags_Leading)) + ImGui::EndTabItem(); + if (ImGui::BeginTabItem("Trailing", NULL, ImGuiTabItemFlags_Trailing)) + ImGui::EndTabItem(); + } + // Demo Trailing Tabs: click the "+" button to add a new tab. // (In your app you may want to use a font icon instead of the "+") // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8d2714772..bb5a79005 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -10220,16 +10220,17 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) } // Horizontal scrolling buttons - // Important: note that TabBarScrollButtons() will alter BarRect.Max.x. + // - Important: note that TabBarScrollButtons() will alter BarRect.Max.x. + // - Selection skips buttons and might cross through sections if there are Tabs in Leading/Trailing sections. const bool can_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed); const float width_all_tabs_to_use_for_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) ? tab_bar->WidthAllTabs : width_all_tabs_after_min_width_shrink; tab_bar->ScrollButtonEnabled = ((width_all_tabs_to_use_for_scroll > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && can_scroll); if (tab_bar->ScrollButtonEnabled) if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar)) { - scroll_to_tab_id = scroll_and_select_tab->ID; if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0) - tab_bar->SelectedTabId = scroll_to_tab_id; + tab_bar->SelectedTabId = scroll_and_select_tab->ID; + scroll_to_tab_id = scroll_and_select_tab->ID; } if (scroll_to_tab_id == 0 && scroll_to_selected_tab) scroll_to_tab_id = tab_bar->SelectedTabId; @@ -10457,6 +10458,13 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id); if (tab == NULL) return; + + // Clamp attempt to scroll to leading/trailing sections items. + // This could be done at the TabBarScrollingButtons() call site as well, but here works. + if (tab->Flags & ImGuiTabItemFlags_Leading) + tab = &tab_bar->Tabs[sections[0].TabCount]; + else if (tab->Flags & ImGuiTabItemFlags_Trailing) + tab = &tab_bar->Tabs[sections[0].TabCount + sections[1].TabCount]; if (tab->Flags & ImGuiTabItemFlags_SectionMask_) return; From 8e174685ae0a1f29626dcf746511a110882cd7cc Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 16 Sep 2026 16:14:06 +0200 Subject: [PATCH 05/25] Backends: added ImGui_ImplXXXX_GetRenderState() helpers to all backends not having one. --- backends/imgui_impl_dx10.h | 2 ++ backends/imgui_impl_dx11.h | 2 ++ backends/imgui_impl_dx12.h | 2 ++ backends/imgui_impl_sdlgpu3.h | 2 ++ backends/imgui_impl_sdlrenderer2.h | 2 ++ backends/imgui_impl_sdlrenderer3.h | 2 ++ backends/imgui_impl_vulkan.h | 2 ++ backends/imgui_impl_wgpu.h | 2 ++ 8 files changed, 16 insertions(+) diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index 179756fb0..427d89b95 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -46,4 +46,6 @@ struct ImGui_ImplDX10_RenderState //ID3D10SamplerState* SamplerNearest; // Use ImDrawList::AddCallback(ImGui::GetPlatform().DrawCallback_SetSamplerNearest) }; +static inline ImGui_ImplDX10_RenderState* ImGui_ImplDX10_GetRenderState() { return (ImGui_ImplDX10_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h index 56166b92a..482b360a2 100644 --- a/backends/imgui_impl_dx11.h +++ b/backends/imgui_impl_dx11.h @@ -49,4 +49,6 @@ struct ImGui_ImplDX11_RenderState //ID3D11SamplerState* SamplerNearest; // Use ImDrawList::AddCallback(ImGui::GetPlatform().DrawCallback_SetSamplerNearest) }; +static inline ImGui_ImplDX11_RenderState* ImGui_ImplDX11_GetRenderState() { return (ImGui_ImplDX11_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index dfb327fe4..fb9085c58 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -75,6 +75,8 @@ struct ImGui_ImplDX12_RenderState ID3D12GraphicsCommandList* CommandList; }; +static inline ImGui_ImplDX12_RenderState* ImGui_ImplDX12_GetRenderState() { return (ImGui_ImplDX12_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy initialization API Obsoleted in 1.91.5 // - font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap' diff --git a/backends/imgui_impl_sdlgpu3.h b/backends/imgui_impl_sdlgpu3.h index bc21d1c75..9c7d56e46 100644 --- a/backends/imgui_impl_sdlgpu3.h +++ b/backends/imgui_impl_sdlgpu3.h @@ -59,4 +59,6 @@ struct ImGui_ImplSDLGPU3_RenderState SDL_GPUDevice* Device; }; +static inline ImGui_ImplSDLGPU3_RenderState* ImGui_ImplSDLGPU3_GetRenderState() { return (ImGui_ImplSDLGPU3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlrenderer2.h b/backends/imgui_impl_sdlrenderer2.h index f33bc160e..0c14912db 100644 --- a/backends/imgui_impl_sdlrenderer2.h +++ b/backends/imgui_impl_sdlrenderer2.h @@ -52,4 +52,6 @@ struct ImGui_ImplSDLRenderer2_RenderState SDL_Renderer* Renderer; }; +static inline ImGui_ImplSDLRenderer2_RenderState* ImGui_ImplSDLRenderer2_GetRenderState() { return (ImGui_ImplSDLRenderer2_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index c01a16d48..d5a3c8e5b 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -50,4 +50,6 @@ struct ImGui_ImplSDLRenderer3_RenderState SDL_Renderer* Renderer; }; +static inline ImGui_ImplSDLRenderer3_RenderState* ImGui_ImplSDLRenderer3_GetRenderState() { return (ImGui_ImplSDLRenderer3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + #endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index cf6a3be15..646d228c9 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -176,6 +176,8 @@ struct ImGui_ImplVulkan_RenderState VkPipelineLayout PipelineLayout; }; +static inline ImGui_ImplVulkan_RenderState* ImGui_ImplVulkan_GetRenderState() { return (ImGui_ImplVulkan_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers //------------------------------------------------------------------------- diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index ed84f5ecb..707c12772 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -82,6 +82,8 @@ struct ImGui_ImplWGPU_RenderState WGPURenderPassEncoder RenderPassEncoder; }; +static inline ImGui_ImplWGPU_RenderState* ImGui_ImplWGPU_GetRenderState() { return (ImGui_ImplWGPU_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } + //------------------------------------------------------------------------- // Internal Helpers // Those are currently used by our example applications. From a5b5f2d85a8779841a2fb181428fe5bd2c4c15d6 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Sun, 13 Sep 2026 13:15:52 +0100 Subject: [PATCH 06/25] Backends: SDLRenderer3: expose CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState. (#9543) --- backends/imgui_impl_sdlrenderer3.cpp | 23 ++++++++++------------- backends/imgui_impl_sdlrenderer3.h | 3 ++- docs/CHANGELOG.txt | 1 + 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index cb975393e..01905202e 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -62,9 +62,6 @@ struct ImGui_ImplSDLRenderer3_Data SDL_Renderer* Renderer; // Main viewport's renderer ImVector ColorBuffer; - // Render State - SDL_ScaleMode CurrentScaleMode; - ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -78,14 +75,14 @@ static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData() // Functions static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) { - ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); // Clear out any viewports and cliprect set by the user // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. SDL_SetRenderViewport(renderer, nullptr); SDL_SetRenderClipRect(renderer, nullptr); - bd->CurrentScaleMode = SDL_SCALEMODE_LINEAR; + render_state->CurrentScaleMode = SDL_SCALEMODE_LINEAR; } void ImGui_ImplSDLRenderer3_NewFrame() @@ -115,8 +112,8 @@ static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVectorCurrentScaleMode = SDL_SCALEMODE_LINEAR; } -static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); bd->CurrentScaleMode = SDL_SCALEMODE_NEAREST; } +static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); render_state->CurrentScaleMode = SDL_SCALEMODE_LINEAR; } +static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); render_state->CurrentScaleMode = SDL_SCALEMODE_NEAREST; } void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) { @@ -159,19 +156,19 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* SDL_GetRenderViewport(renderer, &old.Viewport); SDL_GetRenderClipRect(renderer, &old.ClipRect); - // Setup desired state - ImGui_ImplSDLRenderer3_SetupRenderState(renderer); - // Setup render state structure (for callbacks and custom texture bindings) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplSDLRenderer3_RenderState render_state; render_state.Renderer = renderer; platform_io.Renderer_RenderState = &render_state; + // Setup desired state + ImGui_ImplSDLRenderer3_SetupRenderState(renderer); + // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = render_scale; - SDL_ScaleMode last_scale_mode = bd->CurrentScaleMode; + SDL_ScaleMode last_scale_mode = render_state.CurrentScaleMode; // Render command lists for (const ImDrawList* draw_list : draw_data->CmdLists) @@ -209,12 +206,12 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv)); const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+ - if (last_scale_mode != bd->CurrentScaleMode) + if (last_scale_mode != render_state.CurrentScaleMode) SDL_FlushRenderer(renderer); // Bind texture, Draw SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); - SDL_SetTextureScaleMode(tex, bd->CurrentScaleMode); + SDL_SetTextureScaleMode(tex, render_state.CurrentScaleMode); SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index d5a3c8e5b..7599974d1 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -27,7 +27,7 @@ #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE -struct SDL_Renderer; +#include // Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer); @@ -48,6 +48,7 @@ IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) struct ImGui_ImplSDLRenderer3_RenderState { SDL_Renderer* Renderer; + SDL_ScaleMode CurrentScaleMode; }; static inline ImGui_ImplSDLRenderer3_RenderState* ImGui_ImplSDLRenderer3_GetRenderState() { return (ImGui_ImplSDLRenderer3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 96d6cd99b..ce018600d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -106,6 +106,7 @@ Other Changes: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] - SDL2: fixed querying framebuffer scale/density when using Metal without going through a SDL_Renderer. (#5592) [@lailoken] + - SDLRenderer3: expose CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState. (#9543, #9378) [@Clownacy] - Vulkan: added for support for multiple Vulkan contexts with custom loaders. (#6616) [@lstalmir] - WebGPU: fixed passing non-integer sizes to `wgpuRenderPassEncoderSetViewport()` when when `FramebufferScale` is >1.0f. (#9515, #8628) [@WayU] From 7cb5ded5d2f1b9c7836c62beed79c23af0837f16 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 11:36:33 +0200 Subject: [PATCH 07/25] Backends: SDLRenderer2, SDLRenderer3: reduce includes. (#9543) Amend a5b5f2d --- backends/imgui_impl_sdlrenderer2.cpp | 3 ++- backends/imgui_impl_sdlrenderer3.cpp | 3 ++- backends/imgui_impl_sdlrenderer3.h | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp index e295e8677..e66150e57 100644 --- a/backends/imgui_impl_sdlrenderer2.cpp +++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -55,7 +55,8 @@ #endif // SDL -#include +#include +#include #if !SDL_VERSION_ATLEAST(2,0,17) #error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function #endif diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 01905202e..1a287efa6 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -51,7 +51,8 @@ #endif // SDL -#include +#include +#include #if !SDL_VERSION_ATLEAST(3,0,0) #error This backend requires SDL 3.0.0+ #endif diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index 7599974d1..e4d483ab8 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -27,7 +27,8 @@ #include "imgui.h" // IMGUI_IMPL_API #ifndef IMGUI_DISABLE -#include +// For SDL_ScaleMode which cannot be forward declared. +#include // Follow "Getting Started" link and check examples/ folder to learn about using backends! IMGUI_IMPL_API bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer); From 8d3e37eb5343d4a5aa15718ea0a1889c6e4f2d6c Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 14:32:33 +0200 Subject: [PATCH 08/25] Backends: SDLRenderer3: restore modified texture scale modes at the end of RenderDrawData(). (#9378, #9543) --- backends/imgui_impl_sdlrenderer3.cpp | 59 ++++++++++++++++++++++------ backends/imgui_impl_sdlrenderer3.h | 2 +- docs/CHANGELOG.txt | 3 +- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 1a287efa6..3db26573c 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -25,6 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-09-17: Restore modified texture scale modes at the end of RenderDrawData(). Added CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState (#9543, #9378) // 2026-07-15: Fixed default sampler state to be linear (broken 2026-04-23). (#9470, #9378) // 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 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) @@ -58,10 +59,17 @@ #endif // SDL_Renderer data +struct ImGui_ImplSDLRenderer3_TexScaleModeEntry +{ + SDL_Texture* Texture; + SDL_ScaleMode ScaleModeBackup; +}; + struct ImGui_ImplSDLRenderer3_Data { SDL_Renderer* Renderer; // Main viewport's renderer ImVector ColorBuffer; + ImVector TexScaleModeBackups; // Backup of texture scale modes ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -82,7 +90,6 @@ static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. SDL_SetRenderViewport(renderer, nullptr); SDL_SetRenderClipRect(renderer, nullptr); - render_state->CurrentScaleMode = SDL_SCALEMODE_LINEAR; } @@ -116,6 +123,38 @@ static void ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState(const ImDrawLis static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); render_state->CurrentScaleMode = SDL_SCALEMODE_LINEAR; } static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); render_state->CurrentScaleMode = SDL_SCALEMODE_NEAREST; } +// SDLRenderer has various design issues which makes it hard to do it compared to regular graphics API. +// - scale/filtering mode is tied to a texture. +// - scale/filtering mode is not queued as part of the SDL_RenderGeometryXXX commands, so a change in one texture requires a flush. +static void ImGui_ImplSDLRenderer3_TexScaleModeUpdate(SDL_Renderer* renderer, SDL_Texture* tex, SDL_ScaleMode desired_scale_mode) +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + SDL_ScaleMode tex_scale_mode = desired_scale_mode; + if (!SDL_GetTextureScaleMode(tex, &tex_scale_mode) || tex_scale_mode == desired_scale_mode) + return; + SDL_FlushRenderer(renderer); + SDL_SetTextureScaleMode(tex, desired_scale_mode); + + // Store backup of old scale mode + for (ImGui_ImplSDLRenderer3_TexScaleModeEntry& entry : bd->TexScaleModeBackups) + if (entry.Texture == tex) + return; + ImGui_ImplSDLRenderer3_TexScaleModeEntry entry = { tex, tex_scale_mode }; + bd->TexScaleModeBackups.push_back(entry); +} + +static void ImGui_ImplSDLRenderer3_TexScaleModeRestoreBackups(SDL_Renderer* renderer) +{ + ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); + if (bd->TexScaleModeBackups.Size == 0) + return; + SDL_FlushRenderer(renderer); + for (ImGui_ImplSDLRenderer3_TexScaleModeEntry& entry : bd->TexScaleModeBackups) + if (entry.ScaleModeBackup != SDL_SCALEMODE_INVALID) + SDL_SetTextureScaleMode(entry.Texture, entry.ScaleModeBackup); + bd->TexScaleModeBackups.resize(0); +} + void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); @@ -165,11 +204,11 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* // Setup desired state ImGui_ImplSDLRenderer3_SetupRenderState(renderer); + IM_ASSERT(bd->TexScaleModeBackups.Size == 0); // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = render_scale; - SDL_ScaleMode last_scale_mode = render_state.CurrentScaleMode; // Render command lists for (const ImDrawList* draw_list : draw_data->CmdLists) @@ -199,20 +238,17 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; - SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) }; SDL_SetRenderClipRect(renderer, &r); + // Setup texture scale/filtering mode + SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); + ImGui_ImplSDLRenderer3_TexScaleModeUpdate(renderer, tex, render_state.CurrentScaleMode); + + // Draw const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos)); const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv)); - const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+ - - if (last_scale_mode != render_state.CurrentScaleMode) - SDL_FlushRenderer(renderer); - - // Bind texture, Draw - SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); - SDL_SetTextureScaleMode(tex, render_state.CurrentScaleMode); + const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, xy, (int)sizeof(ImDrawVert), color, (int)sizeof(ImDrawVert), @@ -227,6 +263,7 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* // Restore modified SDL_Renderer state SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr); SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr); + ImGui_ImplSDLRenderer3_TexScaleModeRestoreBackups(renderer); } void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index e4d483ab8..5559a5035 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -49,7 +49,7 @@ IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) struct ImGui_ImplSDLRenderer3_RenderState { SDL_Renderer* Renderer; - SDL_ScaleMode CurrentScaleMode; + SDL_ScaleMode CurrentScaleMode; // Current scale mode during render. }; static inline ImGui_ImplSDLRenderer3_RenderState* ImGui_ImplSDLRenderer3_GetRenderState() { return (ImGui_ImplSDLRenderer3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ce018600d..6ba8bcfdd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -93,7 +93,7 @@ Other Changes: and various operators. - IO: restore default clipboard/ime/shell platform handlers on ClearPlatformHandlers(), fixing issues switching backends mid-session to one relying on default. (#9537, #8945, #2769) [@lstalmir] -- ImDrawList: +- DrawList: - Per-viewport FramebufferScale for Apple/Retina screen is applied to draw lists: - AA Fringe is scaled accordingly. - Circle and Curves tessellation error are scaled accordingly. @@ -106,6 +106,7 @@ Other Changes: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] - SDL2: fixed querying framebuffer scale/density when using Metal without going through a SDL_Renderer. (#5592) [@lailoken] + - SDLRenderer3: restore any temporarily modified texture scale modes at the of `RenderDrawData()`. (#9378) - SDLRenderer3: expose CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState. (#9543, #9378) [@Clownacy] - Vulkan: added for support for multiple Vulkan contexts with custom loaders. (#6616) [@lstalmir] - WebGPU: fixed passing non-integer sizes to `wgpuRenderPassEncoderSetViewport()` From 09b7ff9385c714e177f55df35436193a300652f2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 19:08:01 +0200 Subject: [PATCH 09/25] Backends: rework header comments about ImGui_ImplXXX_GetRenderState() functions. Amend 8e17468 --- backends/imgui_impl_dx10.cpp | 1 + backends/imgui_impl_dx10.h | 1 + backends/imgui_impl_dx11.cpp | 2 +- backends/imgui_impl_dx11.h | 2 +- backends/imgui_impl_dx12.cpp | 2 +- backends/imgui_impl_dx12.h | 2 +- backends/imgui_impl_opengl3.cpp | 1 + backends/imgui_impl_opengl3.h | 1 + backends/imgui_impl_sdlgpu3.cpp | 1 + backends/imgui_impl_sdlgpu3.h | 1 + backends/imgui_impl_sdlrenderer3.cpp | 2 +- backends/imgui_impl_sdlrenderer3.h | 2 +- backends/imgui_impl_vulkan.cpp | 2 +- backends/imgui_impl_vulkan.h | 2 +- backends/imgui_impl_wgpu.cpp | 2 +- backends/imgui_impl_wgpu.h | 2 +- 16 files changed, 16 insertions(+), 10 deletions(-) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 7cf062cc7..cb767d6ac 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplDX10_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx10.h b/backends/imgui_impl_dx10.h index 427d89b95..6f75693f0 100644 --- a/backends/imgui_impl_dx10.h +++ b/backends/imgui_impl_dx10.h @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplDX10_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index a15f1ab25..ff0a607c4 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -5,7 +5,7 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplDX11_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx11.h b/backends/imgui_impl_dx11.h index 482b360a2..2fe392dfa 100644 --- a/backends/imgui_impl_dx11.h +++ b/backends/imgui_impl_dx11.h @@ -5,7 +5,7 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplDX11_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 811600b66..e5b314eb1 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -5,7 +5,7 @@ // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplDX12_GetRenderState(). // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index fb9085c58..a8f3a6cf6 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -5,7 +5,7 @@ // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplDX12_GetRenderState(). // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 6d3e2751f..a3b0502ae 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -7,6 +7,7 @@ // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplOpenGL3_GetRenderState(). // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h index a93804213..175598600 100644 --- a/backends/imgui_impl_opengl3.h +++ b/backends/imgui_impl_opengl3.h @@ -7,6 +7,7 @@ // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplOpenGL3_GetRenderState(). // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. diff --git a/backends/imgui_impl_sdlgpu3.cpp b/backends/imgui_impl_sdlgpu3.cpp index 2a73f1b05..53b47d434 100644 --- a/backends/imgui_impl_sdlgpu3.cpp +++ b/backends/imgui_impl_sdlgpu3.cpp @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'SDL_GPUTexture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! **IMPORTANT** Before 2025/08/08, ImTextureID was a reference to a SDL_GPUTextureSamplerBinding struct. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplSDLGPU3_GetRenderState(). // The aim of imgui_impl_sdlgpu3.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/backends/imgui_impl_sdlgpu3.h b/backends/imgui_impl_sdlgpu3.h index 9c7d56e46..ce216fef0 100644 --- a/backends/imgui_impl_sdlgpu3.h +++ b/backends/imgui_impl_sdlgpu3.h @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'SDL_GPUTexture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! **IMPORTANT** Before 2025/08/08, ImTextureID was a reference to a SDL_GPUTextureSamplerBinding struct. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplSDLGPU3_GetRenderState(). // The aim of imgui_impl_sdlgpu3.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 3db26573c..4e970f97e 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -13,7 +13,7 @@ // [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplSDLRenderer3_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index 5559a5035..13efcba27 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -13,7 +13,7 @@ // [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplSDLRenderer3_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 795c87a1a..9eca9a767 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -5,7 +5,7 @@ // [!] Renderer: User texture binding. Use a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplVulkan_GetRenderState(). // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 646d228c9..a48dae3ec 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -5,7 +5,7 @@ // [!] Renderer: User texture binding. Use a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplVulkan_GetRenderState(). // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index b57035a06..bdc4d434b 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -5,8 +5,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplWGPU_GetRenderState(). // Read imgui_impl_wgpu.h about how to use the IMGUI_IMPL_WEBGPU_BACKEND_WGPU or IMGUI_IMPL_WEBGPU_BACKEND_DAWN flags. diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 707c12772..a5867c923 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -17,8 +17,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). -// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. // [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Expose selected render state for draw callbacks to use. Access with ImGui_ImplWGPU_GetRenderState(). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. From b5135f270f3d43209a9ff4c0075dc46347be52eb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 15:25:54 +0200 Subject: [PATCH 10/25] Backends: SDLRenderer3: added support for platform_io.DrawCallback_SetSamplerFromTex. (#9378) --- backends/imgui_impl_sdlrenderer3.cpp | 46 ++++++++++++++++++++-------- backends/imgui_impl_sdlrenderer3.h | 2 +- docs/CHANGELOG.txt | 8 +++++ imgui.cpp | 2 +- imgui.h | 1 + 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index 4e970f97e..121ce59c8 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -25,7 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2026-09-17: Restore modified texture scale modes at the end of RenderDrawData(). Added CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState (#9543, #9378) +// 2026-09-17: Restore modified texture scale modes at the end of RenderDrawData(). Added DrawCallback_SetSamplerFromTex support. Added CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState (#9543, #9378) // 2026-07-15: Fixed default sampler state to be linear (broken 2026-04-23). (#9470, #9378) // 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 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) @@ -122,6 +122,7 @@ static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVectorCurrentScaleMode = SDL_SCALEMODE_LINEAR; } static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); render_state->CurrentScaleMode = SDL_SCALEMODE_NEAREST; } +static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerFromTex(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_RenderState* render_state = ImGui_ImplSDLRenderer3_GetRenderState(); render_state->CurrentScaleMode = SDL_SCALEMODE_INVALID; } // SDL_SCALEMODE_INVALID = use/keep value in texture. // SDLRenderer has various design issues which makes it hard to do it compared to regular graphics API. // - scale/filtering mode is tied to a texture. @@ -129,18 +130,38 @@ static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawLi static void ImGui_ImplSDLRenderer3_TexScaleModeUpdate(SDL_Renderer* renderer, SDL_Texture* tex, SDL_ScaleMode desired_scale_mode) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); - SDL_ScaleMode tex_scale_mode = desired_scale_mode; - if (!SDL_GetTextureScaleMode(tex, &tex_scale_mode) || tex_scale_mode == desired_scale_mode) - return; - SDL_FlushRenderer(renderer); - SDL_SetTextureScaleMode(tex, desired_scale_mode); - - // Store backup of old scale mode - for (ImGui_ImplSDLRenderer3_TexScaleModeEntry& entry : bd->TexScaleModeBackups) - if (entry.Texture == tex) + if (desired_scale_mode != SDL_SCALEMODE_INVALID) + { + // Enforce a scale mode + SDL_ScaleMode tex_scale_mode = desired_scale_mode; + if (!SDL_GetTextureScaleMode(tex, &tex_scale_mode) || tex_scale_mode == desired_scale_mode) return; - ImGui_ImplSDLRenderer3_TexScaleModeEntry entry = { tex, tex_scale_mode }; - bd->TexScaleModeBackups.push_back(entry); + SDL_FlushRenderer(renderer); + SDL_SetTextureScaleMode(tex, desired_scale_mode); + + // Store backup of old scale mode (FIXME-OPT) + for (ImGui_ImplSDLRenderer3_TexScaleModeEntry& entry : bd->TexScaleModeBackups) + if (entry.Texture == tex) + { + if (entry.ScaleModeBackup == SDL_SCALEMODE_INVALID) + entry.ScaleModeBackup = tex_scale_mode; + return; + } + ImGui_ImplSDLRenderer3_TexScaleModeEntry entry = { tex, tex_scale_mode }; + bd->TexScaleModeBackups.push_back(entry); + } + else + { + // Use scale mode that was set in texture by user. If there is an entry, restore it and clear the entry. + for (ImGui_ImplSDLRenderer3_TexScaleModeEntry& entry : bd->TexScaleModeBackups) + if (entry.Texture == tex && entry.ScaleModeBackup != SDL_SCALEMODE_INVALID) + { + SDL_FlushRenderer(renderer); + SDL_SetTextureScaleMode(tex, entry.ScaleModeBackup); + entry.ScaleModeBackup = SDL_SCALEMODE_INVALID; + return; + } + } } static void ImGui_ImplSDLRenderer3_TexScaleModeRestoreBackups(SDL_Renderer* renderer) @@ -345,6 +366,7 @@ bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) platform_io.DrawCallback_ResetRenderState = ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState; platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerLinear; platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest; + platform_io.DrawCallback_SetSamplerFromTex = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerFromTex; bd->Renderer = renderer; diff --git a/backends/imgui_impl_sdlrenderer3.h b/backends/imgui_impl_sdlrenderer3.h index 13efcba27..019def26a 100644 --- a/backends/imgui_impl_sdlrenderer3.h +++ b/backends/imgui_impl_sdlrenderer3.h @@ -49,7 +49,7 @@ IMGUI_IMPL_API void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) struct ImGui_ImplSDLRenderer3_RenderState { SDL_Renderer* Renderer; - SDL_ScaleMode CurrentScaleMode; // Current scale mode during render. + SDL_ScaleMode CurrentScaleMode; // Current scale mode during render. Set to SDL_SCALEMODE_INVALID to use "SetSamplerFromTex" mode. }; static inline ImGui_ImplSDLRenderer3_RenderState* ImGui_ImplSDLRenderer3_GetRenderState() { return (ImGui_ImplSDLRenderer3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6ba8bcfdd..4ff37a224 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -100,6 +100,9 @@ Other Changes: - Reworked assert in PushTexture() to allow convenience grace period of using a texture that was queue for destroy during the frame. Make it safe to stick to an existing texture during the frame. (#9528, #8465) + - Added optional `platform_io.DrawCallback_SetSamplerFromTex` standard backend-agnostic + draw callback for requesting to use the sampler associated already associated to a texture. + Currently only supported by the SDLRenderer3 backend. (#9378) - Demo: - Added `Widgets->Mixed Values` section. (#5518, #5677, #6865) - Backends: @@ -108,6 +111,11 @@ Other Changes: going through a SDL_Renderer. (#5592) [@lailoken] - SDLRenderer3: restore any temporarily modified texture scale modes at the of `RenderDrawData()`. (#9378) - SDLRenderer3: expose CurrentScaleMode in ImGui_ImplSDLRenderer3_RenderState. (#9543, #9378) [@Clownacy] + - SDLRenderer3: added support for `platform_io.DrawCallback_SetSamplerFromTex` draw callback to request + using the sampler associated already associated to a texture via `SDL_SetTextureScaleMode()`. (#9378) + It is equivalent to setting `render_state->CurrentScaleMode = SDL_SCALEMODE_INVALID`. + Note: this callback is only supported by selected rendering backends: most other graphics API + don't have the equivalent of `SDL_SetTextureScaleMode()` and the callback won't work with them. - Vulkan: added for support for multiple Vulkan contexts with custom loaders. (#6616) [@lstalmir] - WebGPU: fixed passing non-integer sizes to `wgpuRenderPassEncoderSetViewport()` when when `FramebufferScale` is >1.0f. (#9515, #8628) [@WayU] diff --git a/imgui.cpp b/imgui.cpp index 90a80a3f8..9e5880ebd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -16111,7 +16111,7 @@ void ImGuiPlatformIO::ClearRendererHandlers() { Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0; Renderer_RenderState = NULL; - DrawCallback_ResetRenderState = DrawCallback_SetSamplerLinear = DrawCallback_SetSamplerNearest = NULL; + DrawCallback_ResetRenderState = DrawCallback_SetSamplerLinear = DrawCallback_SetSamplerNearest = DrawCallback_SetSamplerFromTex = NULL; } ImGuiViewport* ImGui::GetMainViewport() diff --git a/imgui.h b/imgui.h index fe4f82d58..53f1e8660 100644 --- a/imgui.h +++ b/imgui.h @@ -4114,6 +4114,7 @@ struct ImGuiPlatformIO ImDrawCallback DrawCallback_ResetRenderState; // Request to reset the graphics/render state. ImDrawCallback DrawCallback_SetSamplerLinear; // Request backend to set texture sampling to Linear. ImDrawCallback DrawCallback_SetSamplerNearest; // Request backend to set texture sampling to Nearest/Point. + ImDrawCallback DrawCallback_SetSamplerFromTex; // Request backend to use sampler associated to texture - only available in some backends: SDLRenderer3. //ImDrawCallback DrawCallback_SetSamplerCustom; // Request backend to set texture sampling using Backend Specific data. //------------------------------------------------------------------ From 333f5186e3f9137bfac6487896ff041c870389e8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 19:17:41 +0200 Subject: [PATCH 11/25] Backends: OpenGL2, OpenGL3: fixed `DrawCallback_ResetRenderState` handler not reseting all states in the glTexParameter() path. (#9378) + made OpenGL3 SetSamplerLinear/SetSamplerNearest check UseBindSampler for consistency. --- backends/imgui_impl_opengl2.cpp | 2 ++ backends/imgui_impl_opengl3.cpp | 17 ++++++++--------- backends/imgui_impl_opengl3.h | 4 ++-- docs/CHANGELOG.txt | 2 ++ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 8fe9d489f..713a7b0ba 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -132,6 +132,8 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glShadeModel(GL_SMOOTH); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + bd->UseTexParameterToSetSampler = false; bd->NextSampler = GL_LINEAR; // If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!), diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index a3b0502ae..e45b2c299 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -387,9 +387,12 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_Impl glUniform1i(bd->AttribLocationTex, 0); glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); - (void)render_state; + render_state->UseBindSampler = bd->HasBindSampler; + render_state->UseTexParameterFilter = false; + render_state->CurrentSampler = 0; + render_state->CurrentTexParameterFilter = 0; #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - if (render_state->UseBindSampler) + if (bd->HasBindSampler && render_state->UseBindSampler) { render_state->CurrentSampler = bd->TexSamplers[0]; glBindSampler(0, render_state->CurrentSampler); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise. @@ -419,7 +422,7 @@ static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, c ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState(); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - if (bd->HasBindSampler) + if (bd->HasBindSampler && render_state->UseBindSampler) { render_state->CurrentSampler = bd->TexSamplers[0]; render_state->UseTexParameterFilter = false; @@ -437,7 +440,7 @@ static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState(); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - if (bd->HasBindSampler) + if (bd->HasBindSampler && render_state->UseBindSampler) { render_state->CurrentSampler = bd->TexSamplers[1]; render_state->UseTexParameterFilter = false; @@ -522,11 +525,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // Setup render state structure (for callbacks and custom texture bindings) ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - ImGui_ImplOpenGL3_RenderState render_state; - render_state.UseBindSampler = bd->HasBindSampler; - render_state.UseTexParameterFilter = false; - render_state.CurrentSampler = 0; - render_state.CurrentTexParameterFilter = 0; + ImGui_ImplOpenGL3_RenderState render_state = {}; platform_io.Renderer_RenderState = &render_state; ImGui_ImplOpenGL3_SetupRenderState(draw_data, &render_state, fb_width, fb_height, vertex_array_object); diff --git a/backends/imgui_impl_opengl3.h b/backends/imgui_impl_opengl3.h index 175598600..b5bcc7400 100644 --- a/backends/imgui_impl_opengl3.h +++ b/backends/imgui_impl_opengl3.h @@ -73,8 +73,8 @@ struct ImGui_ImplOpenGL3_RenderState { bool UseBindSampler; bool UseTexParameterFilter; - unsigned int CurrentSampler; // (GLuint) Used if UseBindSampler == true, otherwise always 0 - unsigned int CurrentTexParameterFilter; // (GLuint) Used if UseTexParameterToSetSampler == true + unsigned int CurrentSampler; // (GLuint) Used if UseBindSampler == true (and are available), otherwise always 0 + unsigned int CurrentTexParameterFilter; // (GLuint) Used if UseTexParameterFilter == true }; static inline ImGui_ImplOpenGL3_RenderState* ImGui_ImplOpenGL3_GetRenderState() { return (ImGui_ImplOpenGL3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4ff37a224..a9981e694 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -107,6 +107,8 @@ Other Changes: - Added `Widgets->Mixed Values` section. (#5518, #5677, #6865) - Backends: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] + - OpenGL2, OpenGL3: fixed `DrawCallback_ResetRenderState` handler not reseting all states + in the glTexParameter() path. (#9378) - SDL2: fixed querying framebuffer scale/density when using Metal without going through a SDL_Renderer. (#5592) [@lailoken] - SDLRenderer3: restore any temporarily modified texture scale modes at the of `RenderDrawData()`. (#9378) From 25e16fd8b68eca0ec46a420ea98a6d9ae93b6dbb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 20:22:33 +0200 Subject: [PATCH 12/25] Backends: OpenGL3: minor refactor for both sampler callback to share more code + comments. (#9378) --- backends/imgui_impl_opengl2.cpp | 11 +++--- backends/imgui_impl_opengl3.cpp | 64 ++++++++++++++------------------- 2 files changed, 33 insertions(+), 42 deletions(-) diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 713a7b0ba..503caf992 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -26,7 +26,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2026-09-07: Round framebuffer dimensions to the nearest integer instead of truncating them. (#9538, 9515, #8628) +// 2026-09-07: OpenGL: Round framebuffer dimensions to the nearest integer instead of truncating them. (#9538, 9515, #8628) // 2026-07-15: OpenGL: Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT in UpdateTexture() to avoid corrupting caller GL state. (#8802, #9473) // 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 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) @@ -160,9 +160,12 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid } // Draw callbacks -static void ImGui_ImplOpenGL2_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. -static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } -static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } +// - IMPORTANT: Using DrawCallback_SetSamplerLinear/DrawCallback_SetSamplerNearest will trash glTexParameteri() values for the texture!! +// We cannot decently backup/restore that state because glGetTexParameteriv() is slow on many setups. +// - Using DrawCallback_SetSamplerFromTex will basically instruct the backend to use existing glTexParameteri() values without modifying anything, which is the initial state. +static void ImGui_ImplOpenGL2_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } +static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } // OpenGL2 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index e45b2c299..ed5e7eab3 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -24,7 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2026-09-07: Round framebuffer dimensions to the nearest integer instead of truncating them. (#9538, 9515, #8628) +// 2026-09-07: OpenGL: Round framebuffer dimensions to the nearest integer instead of truncating them. (#9538, 9515, #8628) // 2026-07-15: OpenGL: Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT in UpdateTexture() to avoid corrupting caller GL state. (#8802, #9473) // 2026-06-17: OpenGL: Expose selected render state in ImGui_ImplOpenGL3_RenderState, Allowing to dynamically select between use of glBindSampler() and glTexParameter(). You can access in 'void* platform_io.Renderer_RenderState' during rendering. // 2026-06-03: OpenGL: GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. (#9427, #6577) @@ -415,44 +415,32 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_Impl GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)offsetof(ImDrawVert, col))); } +static void ImGui_ImplOpenGL3_DrawCallback_SetSampler(unsigned int sampler_idx, unsigned int tex_parameter_filter) +{ + ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState(); + IM_UNUSED(sampler_idx); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->HasBindSampler && render_state->UseBindSampler) + { + render_state->CurrentSampler = bd->TexSamplers[sampler_idx]; + render_state->UseTexParameterFilter = false; + glBindSampler(0, render_state->CurrentSampler); + } + else +#endif + { + render_state->UseTexParameterFilter = true; + render_state->CurrentTexParameterFilter = tex_parameter_filter; + } +} + // Draw callbacks -static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. -static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) -{ - ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState(); -#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - if (bd->HasBindSampler && render_state->UseBindSampler) - { - render_state->CurrentSampler = bd->TexSamplers[0]; - render_state->UseTexParameterFilter = false; - glBindSampler(0, render_state->CurrentSampler); - } - else -#endif - { - render_state->UseTexParameterFilter = true; - render_state->CurrentTexParameterFilter = GL_LINEAR; - } -} -static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) -{ - ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState(); -#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER - ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - if (bd->HasBindSampler && render_state->UseBindSampler) - { - render_state->CurrentSampler = bd->TexSamplers[1]; - render_state->UseTexParameterFilter = false; - glBindSampler(0, render_state->CurrentSampler); - } - else -#endif - { - render_state->UseTexParameterFilter = true; - render_state->CurrentTexParameterFilter = GL_NEAREST; - } -} +// - IMPORTANT: Using DrawCallback_SetSamplerLinear/DrawCallback_SetSamplerNearest will trash glTexParameteri() values for the texture if used on a GL context not supporting glBindSampler()!! +// We cannot decently backup/restore that state because glGetTexParameteriv() is slow on many setups. +static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_DrawCallback_SetSampler(0, GL_LINEAR); } +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_DrawCallback_SetSampler(1, GL_NEAREST); } // OpenGL3 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. From eabff0fd081ec1964c8ccab076327bc7c3678dcd Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 20:34:07 +0200 Subject: [PATCH 13/25] Backends: OpenGL2/OpenGL3: Added support for platform_io.DrawCallback_SetSamplerFromTex. (#9378) --- backends/imgui_impl_opengl2.cpp | 3 +++ backends/imgui_impl_opengl3.cpp | 17 +++++++++++++++++ docs/CHANGELOG.txt | 2 ++ imgui.h | 2 +- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 503caf992..88eed8c5f 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -26,6 +26,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-09-17: OpenGL: Added support for platform_io.DrawCallback_SetSamplerFromTex. (#9378) // 2026-09-07: OpenGL: Round framebuffer dimensions to the nearest integer instead of truncating them. (#9538, 9515, #8628) // 2026-07-15: OpenGL: Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT in UpdateTexture() to avoid corrupting caller GL state. (#8802, #9473) // 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) @@ -166,6 +167,7 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid static void ImGui_ImplOpenGL2_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } +static void ImGui_ImplOpenGL2_DrawCallback_SetSamplerFromTex(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData(); bd->UseTexParameterToSetSampler = false; } // OpenGL2 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. @@ -365,6 +367,7 @@ bool ImGui_ImplOpenGL2_Init() platform_io.DrawCallback_ResetRenderState = ImGui_ImplOpenGL2_DrawCallback_ResetRenderState; platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplOpenGL2_DrawCallback_SetSamplerLinear; platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplOpenGL2_DrawCallback_SetSamplerNearest; + platform_io.DrawCallback_SetSamplerFromTex = ImGui_ImplOpenGL2_DrawCallback_SetSamplerFromTex; return true; } diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index ed5e7eab3..1d340f637 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -24,6 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-09-17: OpenGL: Added support for platform_io.DrawCallback_SetSamplerFromTex. (#9378) // 2026-09-07: OpenGL: Round framebuffer dimensions to the nearest integer instead of truncating them. (#9538, 9515, #8628) // 2026-07-15: OpenGL: Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT in UpdateTexture() to avoid corrupting caller GL state. (#8802, #9473) // 2026-06-17: OpenGL: Expose selected render state in ImGui_ImplOpenGL3_RenderState, Allowing to dynamically select between use of glBindSampler() and glTexParameter(). You can access in 'void* platform_io.Renderer_RenderState' during rendering. @@ -438,9 +439,24 @@ static void ImGui_ImplOpenGL3_DrawCallback_SetSampler(unsigned int sampler_idx, // Draw callbacks // - IMPORTANT: Using DrawCallback_SetSamplerLinear/DrawCallback_SetSamplerNearest will trash glTexParameteri() values for the texture if used on a GL context not supporting glBindSampler()!! // We cannot decently backup/restore that state because glGetTexParameteriv() is slow on many setups. +// - Using DrawCallback_SetSamplerFromTex will basically instruct the backend to use existing glTexParameteri() values without modifying anything. static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_DrawCallback_SetSampler(0, GL_LINEAR); } static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_DrawCallback_SetSampler(1, GL_NEAREST); } +static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerFromTex(const ImDrawList*, const ImDrawCmd*) +{ + ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState(); +#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER + ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); + if (bd->HasBindSampler) + { + render_state->CurrentSampler = 0; + glBindSampler(0, 0); + } +#endif + render_state->UseBindSampler = false; + render_state->UseTexParameterFilter = false; +} // OpenGL3 Render function. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. @@ -1083,6 +1099,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) platform_io.DrawCallback_ResetRenderState = ImGui_ImplOpenGL3_DrawCallback_ResetRenderState; platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear; platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest; + platform_io.DrawCallback_SetSamplerFromTex = ImGui_ImplOpenGL3_DrawCallback_SetSamplerFromTex; // Store GLSL version string so we can refer to it later in case we recreate shaders. // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a9981e694..7a0fa2597 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -107,6 +107,8 @@ Other Changes: - Added `Widgets->Mixed Values` section. (#5518, #5677, #6865) - Backends: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] + - OpenGL2, OpenGL3: added support for `platform_io.DrawCallback_SetSamplerFromTex` draw + callback to request using the sampler associated already associated to a texture. (#9378) - OpenGL2, OpenGL3: fixed `DrawCallback_ResetRenderState` handler not reseting all states in the glTexParameter() path. (#9378) - SDL2: fixed querying framebuffer scale/density when using Metal without diff --git a/imgui.h b/imgui.h index 53f1e8660..fe8531083 100644 --- a/imgui.h +++ b/imgui.h @@ -4114,7 +4114,7 @@ struct ImGuiPlatformIO ImDrawCallback DrawCallback_ResetRenderState; // Request to reset the graphics/render state. ImDrawCallback DrawCallback_SetSamplerLinear; // Request backend to set texture sampling to Linear. ImDrawCallback DrawCallback_SetSamplerNearest; // Request backend to set texture sampling to Nearest/Point. - ImDrawCallback DrawCallback_SetSamplerFromTex; // Request backend to use sampler associated to texture - only available in some backends: SDLRenderer3. + ImDrawCallback DrawCallback_SetSamplerFromTex; // Request backend to use sampler associated to texture - only available in some backends: OpenGL2/3 and SDLRenderer3. //ImDrawCallback DrawCallback_SetSamplerCustom; // Request backend to set texture sampling using Backend Specific data. //------------------------------------------------------------------ From 5a0ac1678c298dbff0d4aad7e82fb82ba989b20b Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 17 Sep 2026 21:09:14 +0200 Subject: [PATCH 14/25] Minor rename. --- docs/CHANGELOG.txt | 2 +- imgui_widgets.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7a0fa2597..c83710ec0 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -102,7 +102,7 @@ Other Changes: an existing texture during the frame. (#9528, #8465) - Added optional `platform_io.DrawCallback_SetSamplerFromTex` standard backend-agnostic draw callback for requesting to use the sampler associated already associated to a texture. - Currently only supported by the SDLRenderer3 backend. (#9378) + Currently only supported by the OpenGL2/3 and SDLRenderer3 backend. (#9378) - Demo: - Added `Widgets->Mixed Values` section. (#5518, #5677, #6865) - Backends: diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bb5a79005..0c6c31f5f 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3177,18 +3177,18 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ float clicked_t = 0.0f; if (g.ActiveIdSource == ImGuiInputSource_Mouse) { - const float mouse_abs_pos = g.IO.MousePos[axis]; + const float mouse_pos = g.IO.MousePos[axis]; if (g.ActiveIdIsJustActivated) { float grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize); if (axis == ImGuiAxis_Y) grab_t = 1.0f - grab_t; const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. - g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; + const bool clicked_around_grab = (mouse_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here. + g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_pos - grab_pos : 0.0f; } if (slider_usable_sz > 0.0f) - clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); + clicked_t = ImSaturate((mouse_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); if (axis == ImGuiAxis_Y) clicked_t = 1.0f - clicked_t; set_new_value = true; From 6d07f6eb09b8046269a94902d508f24c594db09c Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 15:49:00 +0200 Subject: [PATCH 15/25] Selectable: fixed `ImGuiSelectableFlags_SelectOnNav` triggering during NavInit. Style Editor: fixed style/colors combo box applying style when opening Combo. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 4 +++- imgui_internal.h | 1 + imgui_widgets.cpp | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c83710ec0..6ec0298bd 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -66,6 +66,8 @@ Other Changes: - Using <> scrolling buttons while the selected Tab is at either end of the section mid-section but not fully visible always makes it fully visible (before eventually selecting a Tab in the leading/trailing section) +- Selectable: + - Fixed `ImGuiSelectableFlags_SelectOnNav` triggering during NavInit. - Fonts: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of @@ -105,6 +107,7 @@ Other Changes: Currently only supported by the OpenGL2/3 and SDLRenderer3 backend. (#9378) - Demo: - Added `Widgets->Mixed Values` section. (#5518, #5677, #6865) + - Style Editor: fixed style/colors combo box applying style when opening Combo. - Backends: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] - OpenGL2, OpenGL3: added support for `platform_io.DrawCallback_SetSamplerFromTex` draw diff --git a/imgui.cpp b/imgui.cpp index 9e5880ebd..c6decca34 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4337,7 +4337,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0; NavJustMovedToKeyMods = ImGuiMod_None; - NavJustMovedToIsTabbing = false; + NavJustMovedToIsTabbing = NavJustMovedToIsInit = false; NavJustMovedToHasSelectionData = false; // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac... @@ -14174,6 +14174,7 @@ void ImGui::NavInitRequestApplyResult() g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = 0; g.NavJustMovedToIsTabbing = false; + g.NavJustMovedToIsInit = true; g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0; } @@ -14441,6 +14442,7 @@ void ImGui::NavMoveRequestApplyResult() g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0; + g.NavJustMovedToIsInit = false; g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0; //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId); } diff --git a/imgui_internal.h b/imgui_internal.h index a248fc800..4a3fdca18 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2445,6 +2445,7 @@ struct ImGuiContext ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyChord NavJustMovedToKeyMods; bool NavJustMovedToIsTabbing; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags. + bool NavJustMovedToIsInit; // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags. bool NavJustMovedToHasSelectionData; // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData. // Navigation: extra config options (will be made public eventually) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0c6c31f5f..1f60853b6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7616,7 +7616,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) // - (2) usage will fail with clipped items // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. - if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId && !g.NavJustMovedToIsInit) if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0) selected = pressed = auto_selected = true; } From f09485d7bed3cc471d2a1310e4fa5b1693af03b7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 16:05:56 +0200 Subject: [PATCH 16/25] Style: inverted ImGuiCol_TitleBgXXX colors for StyleColorsLight(). So that the selected window title bar appears brighter. --- docs/CHANGELOG.txt | 3 +++ imgui_draw.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6ec0298bd..c5c131930 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -81,6 +81,9 @@ Other Changes: - Amended context menu right-click opening code to check that the right-click hasn't been owned by another items. This is more correct and also necessary to avoid Drags/Sliders cancelling also triggering a cancel menu. (#8564, #9534) +- Style: + - Inverted `ImGuiCol_TitleBgXXX` colors for `StyleColorsLight()` so that the selected + window title bar appears brighter. - Misc: - Added `ImGuiItemFlags_MixedValue` flag for representing mixed/indeterminate values. Supported by: Checkbox(), RadioButton(), DragXXX(), SliderXXX(), InputXXX(), ColorXXX() diff --git a/imgui_draw.cpp b/imgui_draw.cpp index bf7aebad1..0e76fcefc 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -336,9 +336,9 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); + colors[ImGuiCol_TitleBg] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.43f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 0.80f); From c8fc1829d0253edaa5b0e5c92a1e8f17b7d15f14 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 16:51:18 +0200 Subject: [PATCH 17/25] ImGuiTextFilter: minor tweaks, made ImGuiTextRange fields match upcoming ImStrv. Removed exposed ImGuiTextRange helpers. Rename CountGrep to CountInclude (#3906) Toward #2435 --- imgui.cpp | 36 ++++++++++++++++++------------------ imgui.h | 26 +++++++++++++------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index c6decca34..43594c4ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3093,7 +3093,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; - CountGrep = 0; + CountInclude = 0; if (default_filter) { ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf)); @@ -3111,7 +3111,7 @@ bool ImGuiTextFilter::Draw(const char* label, float width) return value_changed; } -void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector* out) const +static void ImStrSplit(const char* b, const char* e, char separator, ImVector* out) { out->resize(0); const char* wb = b; @@ -3120,32 +3120,32 @@ void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVectorpush_back(ImGuiTextRange(wb, we)); + out->push_back(ImGuiTextFilter::ImGuiTextRange(wb, we)); wb = we + 1; } we++; } if (wb != we) - out->push_back(ImGuiTextRange(wb, we)); + out->push_back(ImGuiTextFilter::ImGuiTextRange(wb, we)); } void ImGuiTextFilter::Build() { Filters.resize(0); ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf)); - input_range.split(',', &Filters); + ImStrSplit(InputBuf, InputBuf + ImStrlen(InputBuf), ',', &Filters); - CountGrep = 0; + CountInclude = 0; for (ImGuiTextRange& f : Filters) { - while (f.b < f.e && ImCharIsBlankA(f.b[0])) - f.b++; - while (f.e > f.b && ImCharIsBlankA(f.e[-1])) - f.e--; - if (f.empty()) + while (f.Begin < f.End && ImCharIsBlankA(f.Begin[0])) + f.Begin++; + while (f.End > f.Begin && ImCharIsBlankA(f.End[-1])) + f.End--; + if (f.Begin == f.End) continue; - if (f.b[0] != '-') - CountGrep += 1; + if (f.Begin[0] != '-') + CountInclude += 1; } } @@ -3159,24 +3159,24 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const for (const ImGuiTextRange& f : Filters) { - if (f.b == f.e) + if (f.Begin == f.End) continue; - if (f.b[0] == '-') + if (f.Begin[0] == '-') { // Subtract - if (ImStristr(text, text_end, f.b + 1, f.e) != NULL) + if (ImStristr(text, text_end, f.Begin + 1, f.End) != NULL) return false; } else { // Grep - if (ImStristr(text, text_end, f.b, f.e) != NULL) + if (ImStristr(text, text_end, f.Begin, f.End) != NULL) return true; } } // Implicit * grep - if (CountGrep == 0) + if (CountInclude == 0) return true; return false; diff --git a/imgui.h b/imgui.h index fe8531083..a58e944ef 100644 --- a/imgui.h +++ b/imgui.h @@ -2789,26 +2789,26 @@ struct ImGuiOnceUponAFrame struct ImGuiTextFilter { IMGUI_API ImGuiTextFilter(const char* default_filter = ""); - IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; - IMGUI_API void Build(); - void Clear() { InputBuf[0] = 0; Build(); } - bool IsActive() const { return !Filters.empty(); } + IMGUI_API void Build(); // Update internal data when filter changes + inline void Clear() { InputBuf[0] = 0; Build(); } // Clear filter + inline bool IsActive() const { return Filters.Size != 0; } // Useful if you need e.g. an alternative code-path when there are no filters - // [Internal] + // Helper to call InputText() + Build() when buffer is changed. + IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); + + // [Internal] Don't use! Will be replaced with ImStrv. struct ImGuiTextRange { - const char* b; - const char* e; - - ImGuiTextRange() { b = e = NULL; } - ImGuiTextRange(const char* _b, const char* _e) { b = _b; e = _e; } - bool empty() const { return b == e; } - IMGUI_API void split(char separator, ImVector* out) const; + const char* Begin; + const char* End; + ImGuiTextRange(const char* b, const char* e) { Begin = b; End = e; } }; + + // [Internal] Members char InputBuf[256]; ImVectorFilters; - int CountGrep; + int CountInclude; }; // Helper: Growable text buffer for logging/accumulating text From f7ada78e32a1e0758ca209aba7f4ceb2fa9e5b7e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 17:38:24 +0200 Subject: [PATCH 18/25] ImGuiTextFilter: added DrawWithHint(). (#6206, #6395, #6447) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 8 +++++++- imgui.h | 3 ++- imgui_demo.cpp | 21 ++++++++++++++------- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c5c131930..3eecd0dba 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -81,6 +81,9 @@ Other Changes: - Amended context menu right-click opening code to check that the right-click hasn't been owned by another items. This is more correct and also necessary to avoid Drags/Sliders cancelling also triggering a cancel menu. (#8564, #9534) +- ImGuiTextFilter: + - Added `DrawWithHint()` function. (#6206, #6395, #6447) + - Added a "incl,-excl" hint by default when the search field is empty. (#6206, #6395, #6447) - Style: - Inverted `ImGuiCol_TitleBgXXX` colors for `StyleColorsLight()` so that the selected window title bar appears brighter. diff --git a/imgui.cpp b/imgui.cpp index 43594c4ab..aabf2e942 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3105,7 +3105,13 @@ bool ImGuiTextFilter::Draw(const char* label, float width) { if (width != 0.0f) ImGui::SetNextItemWidth(width); - bool value_changed = ImGui::InputText(label, InputBuf, IM_COUNTOF(InputBuf)); + return DrawWithHint(label); +} + +// Use ImGui::SetNextItemWidth() manually if you want to use this. +bool ImGuiTextFilter::DrawWithHint(const char* label, const char* hint) +{ + bool value_changed = ImGui::InputTextWithHint(label, hint, InputBuf, IM_COUNTOF(InputBuf)); if (value_changed) Build(); return value_changed; diff --git a/imgui.h b/imgui.h index a58e944ef..12b0dfb1b 100644 --- a/imgui.h +++ b/imgui.h @@ -2795,7 +2795,8 @@ struct ImGuiTextFilter inline bool IsActive() const { return Filters.Size != 0; } // Useful if you need e.g. an alternative code-path when there are no filters // Helper to call InputText() + Build() when buffer is changed. - IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); + IMGUI_API bool Draw(const char* label = "Filter", float width = 0.0f); + IMGUI_API bool DrawWithHint(const char* label = "Filter", const char* hint = "incl,-excl"); // [Internal] Don't use! Will be replaced with ImStrv. struct ImGuiTextRange diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 7478a99ee..c5e0697aa 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1452,7 +1452,8 @@ static void DemoWindowWidgetsComboBoxes() filter.Clear(); } ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); - filter.Draw("##Filter", -FLT_MIN); + ImGui::SetNextItemWidth(-FLT_MIN); + filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); for (int n = 0; n < IM_COUNTOF(items); n++) { @@ -3945,7 +3946,8 @@ static void DemoWindowWidgetsTextFilter() " \"xxx\" display lines containing \"xxx\"\n" " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" " \"-xxx\" hide lines containing \"xxx\""); - filter.Draw(); + ImGui::SetNextItemWidth(-FLT_MIN); + filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; for (int i = 0; i < IM_COUNTOF(lines); i++) if (filter.PassFilter(lines[i])) @@ -8820,9 +8822,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SameLine(); SetNextItemWidth(GetFontSize() * 10); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); SameLine(); Checkbox("Only Modified Colors", &output_only_modified); - static ImGuiTextFilter filter; - filter.Draw("Filter colors", GetFontSize() * 16); - static ImGuiColorEditFlags alpha_flags = 0; if (RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine(); if (RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } SameLine(); @@ -8832,6 +8831,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "Left-click on color square to open color picker,\n" "Right-click to open edit options menu."); + static ImGuiTextFilter filter; + SetNextItemWidth(-FLT_MIN); + filter.DrawWithHint("##FilterColors", "Filter Colors (incl,-excl)"); + SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); PushItemWidth(GetFontSize() * -12); @@ -9244,7 +9247,10 @@ struct ExampleAppConsole if (ImGui::Button("Options")) ImGui::OpenPopup("Options"); ImGui::SameLine(); - Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); + + ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); + ImGui::SetNextItemWidth(-FLT_MIN); + Filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); ImGui::Separator(); // Reserve enough left-over height for 1 separator + 1 input text @@ -9579,7 +9585,8 @@ struct ExampleAppLog ImGui::SameLine(); bool copy = ImGui::Button("Copy"); ImGui::SameLine(); - Filter.Draw("Filter", -100.0f); + ImGui::SetNextItemWidth(-FLT_MIN); + Filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); ImGui::Separator(); From a14bebd948e021a7801a9d987f135332dc514519 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 17:50:29 +0200 Subject: [PATCH 19/25] (Breaking) ImGuiTextFilter: removed `float width` parameter of `Draw(const char* filter, float width)`. (#6206, #6395, #6447) The reasoning is to (1) keep things consistent with DrawWithHint() without polluting the new function with width. (2) keep things consistent with other items. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 7 +++---- imgui.h | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3eecd0dba..e0d1b70d8 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,8 @@ Breaking Changes: favor of `style.CurveTessellationMaxError` (default 1.12)` which is in Pixels unit. It is easier to tweak + this allows us to easily scale error threshold on high density screens. - style.CurveTessellationMaxError == sqrf(style.CurveTessellationTol). +- ImGuiTextFilter: removed `float width` parameter of `Draw(const char* filter, float width)`: + prefer using `SetNextItemWidth(float)` which is standard. Kept inline redirection function. - Backends: - DirectX12: Removed support for legacy `ImGui_ImplDX12_Init()` signature, which was obsoleted in 1.91.6 (December 2014), because it needed to forcefully disable support diff --git a/imgui.cpp b/imgui.cpp index aabf2e942..d41f4d3fa 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/09/18 (1.93.0) - ImGuiTextFilter: removed `float width` parameter of `Draw(const char* filter, float width)`: prefer using `SetNextItemWidth(float)` which is standard. Kept inline redirection function. - 2026/08/03 (1.93.0) - Style: obsoleted `style.CurveTessellationTol (default 1.25)` which was in Pixels² unit in favor of `style.CurveTessellationMaxError` (default 1.12)` which is in Pixels unit. - style.CurveTessellationMaxError == sqrf(style.CurveTessellationTol). - 2026/07/20 (1.92.9) - DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now defaulting to being disabled: @@ -3101,11 +3102,9 @@ ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 } } -bool ImGuiTextFilter::Draw(const char* label, float width) +bool ImGuiTextFilter::Draw(const char* label) { - if (width != 0.0f) - ImGui::SetNextItemWidth(width); - return DrawWithHint(label); + return DrawWithHint(label, "incl,-excl"); } // Use ImGui::SetNextItemWidth() manually if you want to use this. diff --git a/imgui.h b/imgui.h index 12b0dfb1b..ce48dca17 100644 --- a/imgui.h +++ b/imgui.h @@ -2795,8 +2795,11 @@ struct ImGuiTextFilter inline bool IsActive() const { return Filters.Size != 0; } // Useful if you need e.g. an alternative code-path when there are no filters // Helper to call InputText() + Build() when buffer is changed. - IMGUI_API bool Draw(const char* label = "Filter", float width = 0.0f); + IMGUI_API bool Draw(const char* label = "Filter"); IMGUI_API bool DrawWithHint(const char* label = "Filter", const char* hint = "incl,-excl"); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline bool Draw(const char* label, float width) { if (width != 0.0f) ImGui::SetNextItemWidth(width); return Draw(label); } +#endif // [Internal] Don't use! Will be replaced with ImStrv. struct ImGuiTextRange From fc98dc4a72e196a4a3cb721861651ff882490cac Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 18:31:50 +0200 Subject: [PATCH 20/25] (Internal) ImGuiTextFilter: renaming ImGuiTextRange->ImGuiTextFilterItem, Filters->Items. (#2435) --- imgui.cpp | 26 +++++++++++++------------- imgui.h | 16 ++++++++-------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index d41f4d3fa..b2bdf55f0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3094,7 +3094,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; - CountInclude = 0; + _CountInclude = 0; if (default_filter) { ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf)); @@ -3116,7 +3116,7 @@ bool ImGuiTextFilter::DrawWithHint(const char* label, const char* hint) return value_changed; } -static void ImStrSplit(const char* b, const char* e, char separator, ImVector* out) +static void ImStrSplit(const char* b, const char* e, char separator, ImVector* out) { out->resize(0); const char* wb = b; @@ -3125,23 +3125,23 @@ static void ImStrSplit(const char* b, const char* e, char separator, ImVectorpush_back(ImGuiTextFilter::ImGuiTextRange(wb, we)); + out->push_back(ImGuiTextFilter::ImGuiTextFilterItem(wb, we)); wb = we + 1; } we++; } if (wb != we) - out->push_back(ImGuiTextFilter::ImGuiTextRange(wb, we)); + out->push_back(ImGuiTextFilter::ImGuiTextFilterItem(wb, we)); } void ImGuiTextFilter::Build() { - Filters.resize(0); - ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf)); - ImStrSplit(InputBuf, InputBuf + ImStrlen(InputBuf), ',', &Filters); + _Items.resize(0); + ImGuiTextFilterItem input_range(InputBuf, InputBuf + ImStrlen(InputBuf)); + ImStrSplit(InputBuf, InputBuf + ImStrlen(InputBuf), ',', &_Items); - CountInclude = 0; - for (ImGuiTextRange& f : Filters) + _CountInclude = 0; + for (ImGuiTextFilterItem& f : _Items) { while (f.Begin < f.End && ImCharIsBlankA(f.Begin[0])) f.Begin++; @@ -3150,19 +3150,19 @@ void ImGuiTextFilter::Build() if (f.Begin == f.End) continue; if (f.Begin[0] != '-') - CountInclude += 1; + _CountInclude += 1; } } bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const { - if (Filters.Size == 0) + if (_Items.Size == 0) return true; if (text == NULL) text = text_end = ""; - for (const ImGuiTextRange& f : Filters) + for (const ImGuiTextFilterItem& f : _Items) { if (f.Begin == f.End) continue; @@ -3181,7 +3181,7 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const } // Implicit * grep - if (CountInclude == 0) + if (_CountInclude == 0) return true; return false; diff --git a/imgui.h b/imgui.h index ce48dca17..786afe386 100644 --- a/imgui.h +++ b/imgui.h @@ -2792,7 +2792,7 @@ struct ImGuiTextFilter IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; IMGUI_API void Build(); // Update internal data when filter changes inline void Clear() { InputBuf[0] = 0; Build(); } // Clear filter - inline bool IsActive() const { return Filters.Size != 0; } // Useful if you need e.g. an alternative code-path when there are no filters + inline bool IsActive() const { return _Items.Size != 0; } // Useful if you need e.g. an alternative code-path when there are no filters // Helper to call InputText() + Build() when buffer is changed. IMGUI_API bool Draw(const char* label = "Filter"); @@ -2802,17 +2802,17 @@ struct ImGuiTextFilter #endif // [Internal] Don't use! Will be replaced with ImStrv. - struct ImGuiTextRange + struct ImGuiTextFilterItem { - const char* Begin; - const char* End; - ImGuiTextRange(const char* b, const char* e) { Begin = b; End = e; } + const char* Begin; + const char* End; + ImGuiTextFilterItem(const char* b, const char* e) { Begin = b; End = e; } }; // [Internal] Members - char InputBuf[256]; - ImVectorFilters; - int CountInclude; + char InputBuf[256]; // User input buffer + int _CountInclude; // >= 0 + ImVector _Items; // Pre-parsed, trimmed items }; // Helper: Growable text buffer for logging/accumulating text From 54f86be71ea73b457cf66db600ca881725030131 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 22:02:57 +0200 Subject: [PATCH 21/25] ImGuiTextFilter: fixed filtering when mixing "include" and "-exclude" keyword so that an "-exclude" match always filters out. (#2435) + fix typo in ImToUpper --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 71 +++++++++++++++++++++------------------------- imgui.h | 2 +- imgui_internal.h | 2 +- 4 files changed, 37 insertions(+), 40 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e0d1b70d8..672c24c30 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -84,6 +84,8 @@ Other Changes: hasn't been owned by another items. This is more correct and also necessary to avoid Drags/Sliders cancelling also triggering a cancel menu. (#8564, #9534) - ImGuiTextFilter: + - Fixed filtering when mixing "include" and "-exclude" keyword so that an "-exclude" + match always filters out. (#2435) [@kudaba, @ocornut] - Added `DrawWithHint()` function. (#6206, #6395, #6447) - Added a "incl,-excl" hint by default when the search field is empty. (#6206, #6395, #6447) - Style: diff --git a/imgui.cpp b/imgui.cpp index b2bdf55f0..8968a8f3f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3116,74 +3116,69 @@ bool ImGuiTextFilter::DrawWithHint(const char* label, const char* hint) return value_changed; } -static void ImStrSplit(const char* b, const char* e, char separator, ImVector* out) +static void ImGuiTextFilter_BuildAddItem(ImGuiTextFilter* f, const char* word_b, const char* word_e) { - out->resize(0); - const char* wb = b; - const char* we = wb; - while (we < e) - { - if (*we == separator) - { - out->push_back(ImGuiTextFilter::ImGuiTextFilterItem(wb, we)); - wb = we + 1; - } - we++; - } - if (wb != we) - out->push_back(ImGuiTextFilter::ImGuiTextFilterItem(wb, we)); + // Trim (FIXME: UTF-8 support?) + while (word_b < word_e && ImCharIsBlankA(word_b[0])) + word_b++; + while (word_e > word_b && ImCharIsBlankA(word_e[-1])) + word_e--; + if (word_e - word_b <= 0) + return; + + // Add to list + // FIXME-OPT: on push_front(): as N is derived from user inputs we expect this to be fine. + const bool is_excl = (word_b[0] == '-'); + f->_Items.insert(is_excl ? f->_Items.Data : f->_Items.end(), ImGuiTextFilter::ImGuiTextFilterItem(word_b, word_e)); + if (!is_excl) + f->_CountInclude++; } void ImGuiTextFilter::Build() { + // Parse filters _Items.resize(0); - ImGuiTextFilterItem input_range(InputBuf, InputBuf + ImStrlen(InputBuf)); - ImStrSplit(InputBuf, InputBuf + ImStrlen(InputBuf), ',', &_Items); - _CountInclude = 0; - for (ImGuiTextFilterItem& f : _Items) + const char* buf_e = InputBuf + ImStrlen(InputBuf); + const char* word_b = InputBuf; + const char* word_e = word_b; + while (word_e < buf_e) { - while (f.Begin < f.End && ImCharIsBlankA(f.Begin[0])) - f.Begin++; - while (f.End > f.Begin && ImCharIsBlankA(f.End[-1])) - f.End--; - if (f.Begin == f.End) - continue; - if (f.Begin[0] != '-') - _CountInclude += 1; + if (*word_e == ',') + { + ImGuiTextFilter_BuildAddItem(this, word_b, word_e); + word_b = word_e + 1; + } + word_e++; } + ImGuiTextFilter_BuildAddItem(this, word_b, word_e); } bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const { if (_Items.Size == 0) return true; - if (text == NULL) text = text_end = ""; - for (const ImGuiTextFilterItem& f : _Items) + // Filters are sorted so that '-' ones are always leading. + for (const ImGuiTextFilterItem& item : _Items) { - if (f.Begin == f.End) - continue; - if (f.Begin[0] == '-') + if (item.Begin[0] == '-') { - // Subtract - if (ImStristr(text, text_end, f.Begin + 1, f.End) != NULL) + if (ImStristr(text, text_end, item.Begin + 1, item.End) != NULL) // Exclude return false; } else { - // Grep - if (ImStristr(text, text_end, f.Begin, f.End) != NULL) + if (ImStristr(text, text_end, item.Begin, item.End) != NULL) // Include return true; } } - // Implicit * grep + // When no inclusion are specified (only exclusions) we implicitly pass if (_CountInclude == 0) return true; - return false; } diff --git a/imgui.h b/imgui.h index 786afe386..d66f70d24 100644 --- a/imgui.h +++ b/imgui.h @@ -2812,7 +2812,7 @@ struct ImGuiTextFilter // [Internal] Members char InputBuf[256]; // User input buffer int _CountInclude; // >= 0 - ImVector _Items; // Pre-parsed, trimmed items + ImVector _Items; // Pre-parsed, trimmed, reordered items }; // Helper: Growable text buffer for logging/accumulating text diff --git a/imgui_internal.h b/imgui_internal.h index 4a3fdca18..8b6ffb796 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -407,7 +407,7 @@ IMGUI_API const char* ImStrSkipBlank(const char* str); IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string) IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line IM_MSVC_RUNTIME_CHECKS_OFF -inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } +inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c & ~32 : c; } inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } From c74e7e93a3e0b92bf5da4ebf63f05284c1d32c9b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 19:20:42 +0200 Subject: [PATCH 22/25] ImGuiTextFilter: Added support for space ' ' as separator. Added support for quoted "xxx" blocks. (#2435, #30) --- docs/CHANGELOG.txt | 4 ++- imgui.cpp | 66 ++++++++++++++++++++++++++-------------------- imgui.h | 4 +-- imgui_demo.cpp | 12 ++++----- 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 672c24c30..82c2ffe00 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -84,10 +84,12 @@ Other Changes: hasn't been owned by another items. This is more correct and also necessary to avoid Drags/Sliders cancelling also triggering a cancel menu. (#8564, #9534) - ImGuiTextFilter: + - Added support for space ' ' as separator, which is more standard. (#2435, #30) + - Added support for quoted "xxx" blocks to e.g. match words sequences. (#2435) - Fixed filtering when mixing "include" and "-exclude" keyword so that an "-exclude" match always filters out. (#2435) [@kudaba, @ocornut] - Added `DrawWithHint()` function. (#6206, #6395, #6447) - - Added a "incl,-excl" hint by default when the search field is empty. (#6206, #6395, #6447) + - Added a "incl -excl" hint by default when the search field is empty. (#6206, #6395, #6447) - Style: - Inverted `ImGuiCol_TitleBgXXX` colors for `StyleColorsLight()` so that the selected window title bar appears brighter. diff --git a/imgui.cpp b/imgui.cpp index 8968a8f3f..e0065362c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3090,7 +3090,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // [SECTION] ImGuiTextFilter //----------------------------------------------------------------------------- -// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +// Helper: Parse and apply text filters e.g. 'aaa bbb -ccc'. ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; @@ -3104,7 +3104,7 @@ ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 bool ImGuiTextFilter::Draw(const char* label) { - return DrawWithHint(label, "incl,-excl"); + return DrawWithHint(label, "incl -excl"); } // Use ImGui::SetNextItemWidth() manually if you want to use this. @@ -3118,17 +3118,20 @@ bool ImGuiTextFilter::DrawWithHint(const char* label, const char* hint) static void ImGuiTextFilter_BuildAddItem(ImGuiTextFilter* f, const char* word_b, const char* word_e) { - // Trim (FIXME: UTF-8 support?) - while (word_b < word_e && ImCharIsBlankA(word_b[0])) - word_b++; - while (word_e > word_b && ImCharIsBlankA(word_e[-1])) - word_e--; - if (word_e - word_b <= 0) + // Trim blanks + if (word_b < word_e && word_b[0] != '\"') + { + while (word_b < word_e && ImCharIsBlankA(word_b[0])) // FIXME: UTF-8 support + word_b++; + while (word_e > word_b && ImCharIsBlankA(word_e[-1])) + word_e--; + } + const bool is_excl = (word_b < word_e && word_b[0] == '-'); + if (word_e - word_b - (is_excl ? 1 : 0) <= 0) return; // Add to list - // FIXME-OPT: on push_front(): as N is derived from user inputs we expect this to be fine. - const bool is_excl = (word_b[0] == '-'); + // FIXME-OPT: about ~push_front(): as N is derived from user inputs we expect this to be fine. f->_Items.insert(is_excl ? f->_Items.Data : f->_Items.end(), ImGuiTextFilter::ImGuiTextFilterItem(word_b, word_e)); if (!is_excl) f->_CountInclude++; @@ -3140,18 +3143,26 @@ void ImGuiTextFilter::Build() _Items.resize(0); _CountInclude = 0; const char* buf_e = InputBuf + ImStrlen(InputBuf); - const char* word_b = InputBuf; - const char* word_e = word_b; - while (word_e < buf_e) + const char* word_e; + for (const char* word_b = InputBuf; word_b < buf_e; word_b = word_e + 1) { - if (*word_e == ',') + const bool is_excl = (word_b[0] == '-'); + if (word_b[0] == '\"' || (is_excl && word_b + 1 < buf_e && word_b[1] == '\"')) { - ImGuiTextFilter_BuildAddItem(this, word_b, word_e); - word_b = word_e + 1; + // Parsing "word". Store leading " to distinguish -"word" from "-word", but omit trailing ". + word_e = ImStrchrRange(word_b + (is_excl ? 2 : 1), buf_e, '\"'); + if (word_e == NULL) + word_e = buf_e; } - word_e++; + else + { + // Handle both ' ' and ',' separators. + for (word_e = word_b; word_e < buf_e; word_e++) + if (*word_e == ' ' || *word_e == ',') + break; + } + ImGuiTextFilter_BuildAddItem(this, word_b, word_e); } - ImGuiTextFilter_BuildAddItem(this, word_b, word_e); } bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const @@ -3164,16 +3175,15 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const // Filters are sorted so that '-' ones are always leading. for (const ImGuiTextFilterItem& item : _Items) { - if (item.Begin[0] == '-') - { - if (ImStristr(text, text_end, item.Begin + 1, item.End) != NULL) // Exclude - return false; - } - else - { - if (ImStristr(text, text_end, item.Begin, item.End) != NULL) // Include - return true; - } + const char* word_b = item.Begin; + const char* word_e = item.End; + const bool is_excl = (word_b[0] == '-'); + if (is_excl) + word_b++; + if (word_b < word_e && word_b[0] == '\"') + word_b++; + if (ImStristr(text, text_end, word_b, word_e) != NULL) + return is_excl ? false : true; } // When no inclusion are specified (only exclusions) we implicitly pass diff --git a/imgui.h b/imgui.h index d66f70d24..20fb5fc96 100644 --- a/imgui.h +++ b/imgui.h @@ -2785,7 +2785,7 @@ struct ImGuiOnceUponAFrame operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } }; -// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +// Helper: Parse and apply text filters e.g. 'aaa bbb -ccc'. struct ImGuiTextFilter { IMGUI_API ImGuiTextFilter(const char* default_filter = ""); @@ -2796,7 +2796,7 @@ struct ImGuiTextFilter // Helper to call InputText() + Build() when buffer is changed. IMGUI_API bool Draw(const char* label = "Filter"); - IMGUI_API bool DrawWithHint(const char* label = "Filter", const char* hint = "incl,-excl"); + IMGUI_API bool DrawWithHint(const char* label = "Filter", const char* hint = "incl -excl"); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS inline bool Draw(const char* label, float width) { if (width != 0.0f) ImGui::SetNextItemWidth(width); return Draw(label); } #endif diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c5e0697aa..5ef47f3af 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1453,7 +1453,7 @@ static void DemoWindowWidgetsComboBoxes() } ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F); ImGui::SetNextItemWidth(-FLT_MIN); - filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); + filter.DrawWithHint("##Filter", "Filter (incl -excl)"); for (int n = 0; n < IM_COUNTOF(items); n++) { @@ -3947,7 +3947,7 @@ static void DemoWindowWidgetsTextFilter() " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" " \"-xxx\" hide lines containing \"xxx\""); ImGui::SetNextItemWidth(-FLT_MIN); - filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); + filter.DrawWithHint("##Filter", "Filter (incl -excl)"); const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; for (int i = 0; i < IM_COUNTOF(lines); i++) if (filter.PassFilter(lines[i])) @@ -8833,7 +8833,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) static ImGuiTextFilter filter; SetNextItemWidth(-FLT_MIN); - filter.DrawWithHint("##FilterColors", "Filter Colors (incl,-excl)"); + filter.DrawWithHint("##FilterColors", "Filter Colors (incl -excl)"); SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); @@ -9250,7 +9250,7 @@ struct ExampleAppConsole ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); ImGui::SetNextItemWidth(-FLT_MIN); - Filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); + Filter.DrawWithHint("##Filter", "Filter (incl -excl)"); ImGui::Separator(); // Reserve enough left-over height for 1 separator + 1 input text @@ -9586,7 +9586,7 @@ struct ExampleAppLog bool copy = ImGui::Button("Copy"); ImGui::SameLine(); ImGui::SetNextItemWidth(-FLT_MIN); - Filter.DrawWithHint("##Filter", "Filter (incl,-excl)"); + Filter.DrawWithHint("##Filter", "Filter (incl -excl)"); ImGui::Separator(); @@ -9781,7 +9781,7 @@ struct ExampleAppPropertyEditor ImGui::Text("(%d root nodes)", root_node->Childs.Size); ImGui::SetNextItemWidth(-FLT_MIN); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip); - if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) + if (ImGui::InputTextWithHint("##Filter", "incl -excl", Filter.InputBuf, IM_COUNTOF(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll)) Filter.Build(); ImGui::PopItemFlag(); From c0041b7d3c1e29dda8f173f54dbbe15b285ff62b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 19:29:02 +0200 Subject: [PATCH 23/25] ImGuiTextFilter: added undocumented MinWordSize support. (#2435) --- imgui.cpp | 3 ++- imgui.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index e0065362c..171097ef4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3094,6 +3094,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; + MinWordSize = 1; _CountInclude = 0; if (default_filter) { @@ -3127,7 +3128,7 @@ static void ImGuiTextFilter_BuildAddItem(ImGuiTextFilter* f, const char* word_b, word_e--; } const bool is_excl = (word_b < word_e && word_b[0] == '-'); - if (word_e - word_b - (is_excl ? 1 : 0) <= 0) + if (word_e - word_b - (is_excl ? 1 : 0) < f->MinWordSize) return; // Add to list diff --git a/imgui.h b/imgui.h index 20fb5fc96..c29e97d9d 100644 --- a/imgui.h +++ b/imgui.h @@ -2811,6 +2811,7 @@ struct ImGuiTextFilter // [Internal] Members char InputBuf[256]; // User input buffer + ImU8 MinWordSize; // == 1 int _CountInclude; // >= 0 ImVector _Items; // Pre-parsed, trimmed, reordered items }; From 565564bb5ce7ef0b194ed935e5d002d1ee499cd1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 20:41:16 +0200 Subject: [PATCH 24/25] ImGuiTextFilter: reworked items parsing/storage to be able to simplify PassFilter(). As PassFilter() is the bottleneck. --- imgui.cpp | 69 ++++++++++++++++++++++++------------------------------- imgui.h | 1 + 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 171097ef4..b017bcd5f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3095,7 +3095,7 @@ ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; MinWordSize = 1; - _CountInclude = 0; + _CountExclude = _CountInclude = 0; if (default_filter) { ImStrncpy(InputBuf, default_filter, IM_COUNTOF(InputBuf)); @@ -3117,41 +3117,26 @@ bool ImGuiTextFilter::DrawWithHint(const char* label, const char* hint) return value_changed; } -static void ImGuiTextFilter_BuildAddItem(ImGuiTextFilter* f, const char* word_b, const char* word_e) -{ - // Trim blanks - if (word_b < word_e && word_b[0] != '\"') - { - while (word_b < word_e && ImCharIsBlankA(word_b[0])) // FIXME: UTF-8 support - word_b++; - while (word_e > word_b && ImCharIsBlankA(word_e[-1])) - word_e--; - } - const bool is_excl = (word_b < word_e && word_b[0] == '-'); - if (word_e - word_b - (is_excl ? 1 : 0) < f->MinWordSize) - return; - - // Add to list - // FIXME-OPT: about ~push_front(): as N is derived from user inputs we expect this to be fine. - f->_Items.insert(is_excl ? f->_Items.Data : f->_Items.end(), ImGuiTextFilter::ImGuiTextFilterItem(word_b, word_e)); - if (!is_excl) - f->_CountInclude++; -} - +// Parse filter and split into items void ImGuiTextFilter::Build() { - // Parse filters _Items.resize(0); - _CountInclude = 0; + _CountExclude = _CountInclude = 0; const char* buf_e = InputBuf + ImStrlen(InputBuf); const char* word_e; for (const char* word_b = InputBuf; word_b < buf_e; word_b = word_e + 1) { - const bool is_excl = (word_b[0] == '-'); - if (word_b[0] == '\"' || (is_excl && word_b + 1 < buf_e && word_b[1] == '\"')) + // Trim blanks + while (word_b < buf_e && ImCharIsBlankA(word_b[0])) // FIXME: UTF-8 support + word_b++; + const bool is_excl = (word_b < buf_e && word_b[0] == '-'); + if (is_excl) + word_b++; + const bool is_quote = (word_b < buf_e && word_b[0] == '\"'); + if (is_quote) { - // Parsing "word". Store leading " to distinguish -"word" from "-word", but omit trailing ". - word_e = ImStrchrRange(word_b + (is_excl ? 2 : 1), buf_e, '\"'); + // Parsing quotes. Omit storing leading/trailing quotes. + word_e = ImStrchrRange(++word_b, buf_e, '\"'); if (word_e == NULL) word_e = buf_e; } @@ -3162,7 +3147,19 @@ void ImGuiTextFilter::Build() if (*word_e == ' ' || *word_e == ',') break; } - ImGuiTextFilter_BuildAddItem(this, word_b, word_e); + + // Min length + if (word_e - word_b < MinWordSize) + continue; + + // Add to list + // The '-' is not stored in items but implicitly inferred using (n < CountExclude). + // FIXME-OPT: about ~push_front(): as N is derived from user inputs we expect this to be fine. + _Items.insert(is_excl ? _Items.Data : _Items.Data + _Items.Size, ImGuiTextFilter::ImGuiTextFilterItem(word_b, word_e)); + if (is_excl) + _CountExclude++; + else + _CountInclude++; } } @@ -3174,17 +3171,11 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const text = text_end = ""; // Filters are sorted so that '-' ones are always leading. - for (const ImGuiTextFilterItem& item : _Items) + for (int n = 0; n < _Items.Size; n++) { - const char* word_b = item.Begin; - const char* word_e = item.End; - const bool is_excl = (word_b[0] == '-'); - if (is_excl) - word_b++; - if (word_b < word_e && word_b[0] == '\"') - word_b++; - if (ImStristr(text, text_end, word_b, word_e) != NULL) - return is_excl ? false : true; + const ImGuiTextFilterItem& item = _Items[n]; + if (ImStristr(text, text_end, item.Begin, item.End) != NULL) + return (n < _CountExclude) ? false : true; } // When no inclusion are specified (only exclusions) we implicitly pass diff --git a/imgui.h b/imgui.h index c29e97d9d..26afa5e7a 100644 --- a/imgui.h +++ b/imgui.h @@ -2812,6 +2812,7 @@ struct ImGuiTextFilter // [Internal] Members char InputBuf[256]; // User input buffer ImU8 MinWordSize; // == 1 + int _CountExclude; // >= 0 int _CountInclude; // >= 0 ImVector _Items; // Pre-parsed, trimmed, reordered items }; From 249e5a939dc6df0c31ac5d5a117ab27d3dc8ae2f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 18 Sep 2026 21:04:32 +0200 Subject: [PATCH 25/25] ImGuiTextFilter: added undocumented FilterOp support. (#2435) --- imgui.cpp | 19 ++++++++++++++----- imgui.h | 1 + 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index b017bcd5f..f9dc89e53 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3094,6 +3094,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077 { InputBuf[0] = 0; + FilterOp = '|'; MinWordSize = 1; _CountExclude = _CountInclude = 0; if (default_filter) @@ -3122,6 +3123,7 @@ void ImGuiTextFilter::Build() { _Items.resize(0); _CountExclude = _CountInclude = 0; + IM_ASSERT(FilterOp == '|' || FilterOp == '&'); const char* buf_e = InputBuf + ImStrlen(InputBuf); const char* word_e; for (const char* word_b = InputBuf; word_b < buf_e; word_b = word_e + 1) @@ -3171,17 +3173,24 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const text = text_end = ""; // Filters are sorted so that '-' ones are always leading. - for (int n = 0; n < _Items.Size; n++) + int n; + for (n = 0; n < _CountExclude; n++) + if (ImStristr(text, text_end, _Items.Data[n].Begin, _Items.Data[n].End) != NULL) + return false; + const bool is_and_filter = (FilterOp == '&'); + for (; n < _Items.Size; n++) { - const ImGuiTextFilterItem& item = _Items[n]; - if (ImStristr(text, text_end, item.Begin, item.End) != NULL) - return (n < _CountExclude) ? false : true; + const bool is_match = ImStristr(text, text_end, _Items.Data[n].Begin, _Items.Data[n].End) != NULL; + if (is_match && !is_and_filter) // or incl 1 -> true + return true; // or incl 0 -> continue + if (!is_match && is_and_filter) // and incl 1 -> continue + return false; // and incl 0 -> false } // When no inclusion are specified (only exclusions) we implicitly pass if (_CountInclude == 0) return true; - return false; + return is_and_filter; } //----------------------------------------------------------------------------- diff --git a/imgui.h b/imgui.h index 26afa5e7a..2ad3b91fb 100644 --- a/imgui.h +++ b/imgui.h @@ -2811,6 +2811,7 @@ struct ImGuiTextFilter // [Internal] Members char InputBuf[256]; // User input buffer + char FilterOp; // == '|' (any) pr '&' (all) ImU8 MinWordSize; // == 1 int _CountExclude; // >= 0 int _CountInclude; // >= 0