From 1ce75e2bcad6f85efc3a91a795837ed3489ed9dc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 11:25:15 +0200 Subject: [PATCH 01/11] Fixed duplicate symbols in some compile-time configurations. --- imgui_demo.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 0b7c45aca..899932066 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -10776,7 +10776,6 @@ void ImGui::ShowDemoWindow(bool*) {} void ImGui::ShowUserGuide() {} void ImGui::ShowStyleEditor(ImGuiStyle*) {} bool ImGui::ShowStyleSelector(const char*) { return false; } -void ImGui::ShowFontSelector(const char*) {} #endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS From 41f4acfb4ff259d97eecdf031b44c4853e54e8fc Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 11:44:11 +0200 Subject: [PATCH 02/11] Fonts: add has_textures parameters to ImFontAtlasUpdateNewFrame(). --- docs/CHANGELOG.txt | 4 ++-- imgui.cpp | 7 +++---- imgui_draw.cpp | 11 ++++++----- imgui_internal.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7cda2db6c..20377e2aa 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -115,8 +115,8 @@ Breaking changes: to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this) However you may set TexMinWidth = TexMaxWidth for the same effect. - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on - ImGuiContext to create one, you'll need to set the atlas->RendererHasTextures field - and call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. + ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself. + An assert will trigger if you don't. - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using PushFontSize(style.FontSizeBase * factor) or to manipulate other scaling factors. - Fonts: obsoleted ImFont::Scale which is not useful anymore. diff --git a/imgui.cpp b/imgui.cpp index 90f9ecd2d..f57c305b4 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -470,7 +470,7 @@ CODE - Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling. - Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese(). - Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327) - - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one, you'll need to set the atlas->RendererHasTextures field and call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. + - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one, you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't. - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFontSize(style.FontSizeBase * factor)' or to manipulate other scaling factors. - Fonts: obsoleted ImFont::Scale which is not useful anymore. - Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things: @@ -5286,13 +5286,12 @@ static void ImGui::UpdateTexturesNewFrame() { if (atlas->OwnerContext == &g) { - atlas->RendererHasTextures = has_textures; - ImFontAtlasUpdateNewFrame(atlas, g.FrameCount); + ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures); } else { IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1 && "If you manage font atlases yourself you need to call ImFontAtlasUpdateNewFrame() on it."); - IM_ASSERT(atlas->RendererHasTextures == has_textures && "If you manage font atlases yourself make sure atlas->RendererHasTextures is set consistently with all contexts using it."); + IM_ASSERT(atlas->RendererHasTextures == has_textures && "If you manage font atlases yourself make sure ImGuiBackendFlags_RendererHasTextures is set consistently with atlas->RendererHasTextures as specified in the ImFontAtlasUpdateNewFrame() call."); } } } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2e9a0b645..cd3d22ef9 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2716,12 +2716,13 @@ static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* at } // Called by NewFrame() for atlases owned by a context. -// If you manually manage font atlases, you'll need to call this yourself + ensure atlas->RendererHasTextures is set. -// 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age. -// 'frame_count' may not match those of imgui contexts using this atlas, as contexts may be updated as different frequencies. -void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count) +// If you manually manage font atlases, you'll need to call this yourself. +// - 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age. +// - 'frame_count' may not match those of all imgui contexts using this atlas, as contexts may be updated as different frequencies. But generally you can use ImGui::GetFrameCount() on one of your context. +void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures) { - IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice? + IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice. + atlas->RendererHasTextures = renderer_has_textures; // Check that font atlas was built or backend support texture reload in which case we can build now if (atlas->RendererHasTextures) diff --git a/imgui_internal.h b/imgui_internal.h index b80bf50b6..1d572d5a6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3833,7 +3833,7 @@ IMGUI_API ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtl IMGUI_API ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id); IMGUI_API void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id); -IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count); +IMGUI_API void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures); IMGUI_API void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data); IMGUI_API void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data); IMGUI_API void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex); From 115a8e74c2478d372d09b6ecc2170818fbced396 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 14:18:23 +0200 Subject: [PATCH 03/11] Fonts: update misc comments, docs. --- .github/ISSUE_TEMPLATE/issue_template.yml | 2 +- docs/FONTS.md | 3 +- imgui.cpp | 35 +++++++++++++++-------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/issue_template.yml b/.github/ISSUE_TEMPLATE/issue_template.yml index 46ed826d5..6ed62493a 100644 --- a/.github/ISSUE_TEMPLATE/issue_template.yml +++ b/.github/ISSUE_TEMPLATE/issue_template.yml @@ -4,7 +4,7 @@ body: - type: markdown attributes: value: | - FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING or LOADING FONTS, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions) + FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions) For anything else: **we are happy to use 'GitHub Issues' for many types of open-ended questions**. We are encouraging 'Issues' becoming a large, centralized, tagged, cross-referenced database of Dear ImGui contents. Be mindful that messages are being sent to the e-mail box of "Watching" users. Try to proof-read your messages before sending them. Edits are not seen by those users. diff --git a/docs/FONTS.md b/docs/FONTS.md index 7ed8fe026..95538f97d 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -105,6 +105,7 @@ io.Fonts->AddFontDefault(); ``` **Load .TTF/.OTF file with:** + 🆕 **Since 1.92, with an up to date backend: passing a size is not necessary** ```cpp ImGuiIO& io = ImGui::GetIO(); @@ -377,7 +378,7 @@ TL;DR; With the new system, it is recommended that you create a custom `ImFontLo You can ask questions in [#8466](https://github.com/ocornut/imgui/issues/8466). -🆕 **Before 1.92:** +**Before 1.92:** As an alternative to rendering colorful glyphs using imgui_freetype with `ImGuiFreeTypeBuilderFlags_LoadColor`, you may allocate your own space in the texture atlas and write yourself into it. **(This is a BETA api, use if you are familiar with dear imgui and with your rendering backend)** diff --git a/imgui.cpp b/imgui.cpp index f57c305b4..f7d2e7441 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -21,9 +21,10 @@ // - Issues & support ........... https://github.com/ocornut/imgui/issues // - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps) -// For first-time users having issues compiling/linking/running/loading fonts: +// For first-time users having issues compiling/linking/running: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. // Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. +// Since 1.92, we encourage font loading question to also be posted in 'Issues'. // Copyright (c) 2014-2025 Omar Cornut // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. @@ -347,12 +348,12 @@ CODE ImGui::Render(); // Update textures - for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + ImDrawData* draw_data = ImGui::GetDrawData(); + for (ImTextureData* tex : *draw_data->Textures) if (tex->Status != ImTextureStatus_OK) MyImGuiBackend_UpdateTexture(tex); // Render dear imgui contents, swap buffers - ImDrawData* draw_data = ImGui::GetDrawData(); MyImGuiBackend_RenderDrawData(draw_data); SwapBuffers(); } @@ -372,25 +373,32 @@ CODE { if (tex->Status == ImTextureStatus_WantCreate) { - // create texture based on tex->Width/Height/Pixels - // call tex->SetTexID() to specify backend-specific identifiers - // tex->Status = ImTextureStatus_OK; + // Width/Height/Pixels> + tex->SetTexID(xxxx); // specify backend-specific ImTextureID identifier + tex->SetStatus(ImTextureStatus_OK); + tex->BackendUserData = xxxx; // store more backend data } if (tex->Status == ImTextureStatus_WantUpdates) { - // update texture blocks based on tex->UpdateRect - // tex->Status = ImTextureStatus_OK; + // UpdateRect> + tex->SetStatus(ImTextureStatus_OK); } if (tex->Status == ImTextureStatus_WantDestroy) { - // destroy texture - // call tex->SetTexID(ImTextureID_Invalid) - // tex->Status = ImTextureStatus_Destroyed; + // + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); } } void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data) { + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + MyImGuiBackend_UpdateTexture(tex); + + // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering. // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize @@ -407,7 +415,10 @@ CODE const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) { - pcmd->UserCallback(cmd_list, pcmd); + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + MyEngineResetRenderState(); + else + pcmd->UserCallback(cmd_list, pcmd); } else { From b178fd42862f4314370a32c252c8fbd54eee1127 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 14:55:46 +0200 Subject: [PATCH 04/11] Backends: WebGPU: moved sampler creation out of ImGui_ImplWGPU_CreateFontsTexture(). --- backends/imgui_impl_wgpu.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 3a182ccd2..d5ac95d50 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -597,20 +597,6 @@ static void ImGui_ImplWGPU_CreateFontsTexture() wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size); } - // Create the associated sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - { - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; - sampler_desc.maxAnisotropy = 1; - bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); - } - // Store our identifier static_assert(sizeof(ImTextureID) >= sizeof(bd->renderResources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); io.Fonts->SetTexID((ImTextureID)bd->renderResources.FontTextureView); @@ -760,13 +746,24 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() ImGui_ImplWGPU_CreateFontsTexture(); ImGui_ImplWGPU_CreateUniformBuffer(); + // Create sampler + // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + WGPUSamplerDescriptor sampler_desc = {}; + sampler_desc.minFilter = WGPUFilterMode_Linear; + sampler_desc.magFilter = WGPUFilterMode_Linear; + sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; + sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; + sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; + sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; + sampler_desc.maxAnisotropy = 1; + bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); + // Create resource bind group WGPUBindGroupEntry common_bg_entries[] = { { nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 }, { nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 }, }; - WGPUBindGroupDescriptor common_bg_descriptor = {}; common_bg_descriptor.layout = bg_layouts[0]; common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry); From 571dae9664ee842d88b3139476f1cb85fdc7b48a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 15:12:07 +0200 Subject: [PATCH 05/11] Backends: WGPU: added ImGuiBackendFlags_RendererHasTextures support. (#8465) --- backends/imgui_impl_wgpu.cpp | 132 +++++++++++++++++++++-------------- backends/imgui_impl_wgpu.h | 6 +- docs/CHANGELOG.txt | 2 +- 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index d5ac95d50..09bd30ca8 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -6,8 +6,7 @@ // [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'. -// Missing features or Issues: -// [ ] Renderer: Missing texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -19,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-06-12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. (#8465) // 2025-02-26: Recreate image bind groups during render. (#8426, #8046, #7765, #8027) + Update for latest webgpu-native changes. // 2024-10-14: Update Dawn support for change of string usages. (#8082, #8083) // 2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. @@ -73,11 +73,15 @@ extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed); #define MEMALIGN(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align (copied from IM_ALIGN() macro). // WebGPU data +struct ImGui_ImplWGPU_Texture +{ + WGPUTexture Texture = nullptr; + WGPUTextureView TextureView = nullptr; +}; + struct RenderResources { - WGPUTexture FontTexture = nullptr; // Font texture - WGPUTextureView FontTextureView = nullptr; // Texture view for font texture - WGPUSampler Sampler = nullptr; // Sampler for the font texture + WGPUSampler Sampler = nullptr; // Sampler for textures WGPUBuffer Uniforms = nullptr; // Shader uniforms WGPUBindGroup CommonBindGroup = nullptr; // Resources bind-group to bind the common resources to pipeline ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) @@ -234,23 +238,8 @@ static void SafeRelease(WGPUShaderModule& res) wgpuShaderModuleRelease(res); res = nullptr; } -static void SafeRelease(WGPUTextureView& res) -{ - if (res) - wgpuTextureViewRelease(res); - res = nullptr; -} -static void SafeRelease(WGPUTexture& res) -{ - if (res) - wgpuTextureRelease(res); - res = nullptr; -} - static void SafeRelease(RenderResources& res) { - SafeRelease(res.FontTexture); - SafeRelease(res.FontTextureView); SafeRelease(res.Sampler); SafeRelease(res.Uniforms); SafeRelease(res.CommonBindGroup); @@ -381,6 +370,13 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0) return; + // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do. + // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates). + if (draw_data->Textures != nullptr) + for (ImTextureData* tex : *draw_data->Textures) + if (tex->Status != ImTextureStatus_OK) + ImGui_ImplWGPU_UpdateTexture(tex); + // FIXME: Assuming that this only gets called once per frame! // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); @@ -536,33 +532,52 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder platform_io.Renderer_RenderState = nullptr; } -static void ImGui_ImplWGPU_CreateFontsTexture() +static void ImGui_ImplWGPU_DestroyTexture(ImTextureData* tex) { - // Build texture atlas - ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height, size_pp; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size_pp); + ImGui_ImplWGPU_Texture* backend_tex = (ImGui_ImplWGPU_Texture*)tex->BackendUserData; + if (backend_tex == nullptr) + return; - // Upload texture to graphics system + IM_ASSERT(backend_tex->TextureView == (WGPUTextureView)(intptr_t)tex->TexID); + wgpuTextureViewRelease(backend_tex->TextureView); + wgpuTextureRelease(backend_tex->Texture); + IM_DELETE(backend_tex); + + // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running) + tex->SetTexID(ImTextureID_Invalid); + tex->SetStatus(ImTextureStatus_Destroyed); + tex->BackendUserData = nullptr; +} + +void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex) +{ + ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); + if (tex->Status == ImTextureStatus_WantCreate) { + // Create and upload new texture to graphics system + //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height); + IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + ImGui_ImplWGPU_Texture* backend_tex = IM_NEW(ImGui_ImplWGPU_Texture)(); + + // Create texture WGPUTextureDescriptor tex_desc = {}; #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) - tex_desc.label = { "Dear ImGui Font Texture", WGPU_STRLEN }; + tex_desc.label = { "Dear ImGui Texture", WGPU_STRLEN }; #else - tex_desc.label = "Dear ImGui Font Texture"; + tex_desc.label = "Dear ImGui Texture"; #endif tex_desc.dimension = WGPUTextureDimension_2D; - tex_desc.size.width = width; - tex_desc.size.height = height; + tex_desc.size.width = tex->Width; + tex_desc.size.height = tex->Height; tex_desc.size.depthOrArrayLayers = 1; tex_desc.sampleCount = 1; tex_desc.format = WGPUTextureFormat_RGBA8Unorm; tex_desc.mipLevelCount = 1; tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding; - bd->renderResources.FontTexture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc); + backend_tex->Texture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc); + // Create texture view WGPUTextureViewDescriptor tex_view_desc = {}; tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm; tex_view_desc.dimension = WGPUTextureViewDimension_2D; @@ -571,19 +586,35 @@ static void ImGui_ImplWGPU_CreateFontsTexture() tex_view_desc.baseArrayLayer = 0; tex_view_desc.arrayLayerCount = 1; tex_view_desc.aspect = WGPUTextureAspect_All; - bd->renderResources.FontTextureView = wgpuTextureCreateView(bd->renderResources.FontTexture, &tex_view_desc); + backend_tex->TextureView = wgpuTextureCreateView(backend_tex->Texture, &tex_view_desc); + + // Store identifiers + tex->SetTexID((ImTextureID)(intptr_t)backend_tex->TextureView); + tex->BackendUserData = backend_tex; + // We don't set tex->Status to ImTextureStatus_OK to let the code fallthrough below. } - // Upload texture data + if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates) { + ImGui_ImplWGPU_Texture* backend_tex = (ImGui_ImplWGPU_Texture*)tex->BackendUserData; + IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); + + // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture. + const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x; + const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y; + const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w; + const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h; + + // Update full texture or selected blocks. We only ever write to textures regions which have never been used before! + // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) WGPUTexelCopyTextureInfo dst_view = {}; #else WGPUImageCopyTexture dst_view = {}; #endif - dst_view.texture = bd->renderResources.FontTexture; + dst_view.texture = backend_tex->Texture; dst_view.mipLevel = 0; - dst_view.origin = { 0, 0, 0 }; + dst_view.origin = { (uint32_t)upload_x, (uint32_t)upload_y, 0 }; dst_view.aspect = WGPUTextureAspect_All; #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) WGPUTexelCopyBufferLayout layout = {}; @@ -591,15 +622,14 @@ static void ImGui_ImplWGPU_CreateFontsTexture() WGPUTextureDataLayout layout = {}; #endif layout.offset = 0; - layout.bytesPerRow = width * size_pp; - layout.rowsPerImage = height; - WGPUExtent3D size = { (uint32_t)width, (uint32_t)height, 1 }; - wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size); + layout.bytesPerRow = tex->Width * tex->BytesPerPixel; + layout.rowsPerImage = upload_h; + WGPUExtent3D write_size = { (uint32_t)upload_w, (uint32_t)upload_h, 1 }; + wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, tex->GetPixelsAt(upload_x, upload_y), (uint32_t)(tex->Width * upload_h * tex->BytesPerPixel), &layout, &write_size); + tex->SetStatus(ImTextureStatus_OK); } - - // Store our identifier - static_assert(sizeof(ImTextureID) >= sizeof(bd->renderResources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); - io.Fonts->SetTexID((ImTextureID)bd->renderResources.FontTextureView); + if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) + ImGui_ImplWGPU_DestroyTexture(tex); } static void ImGui_ImplWGPU_CreateUniformBuffer() @@ -743,7 +773,6 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc); - ImGui_ImplWGPU_CreateFontsTexture(); ImGui_ImplWGPU_CreateUniformBuffer(); // Create sampler @@ -788,8 +817,10 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects() SafeRelease(bd->pipelineState); SafeRelease(bd->renderResources); - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->SetTexID(0); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. + // Destroy all textures + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) + if (tex->RefCount == 1) + ImGui_ImplWGPU_DestroyTexture(tex); for (unsigned int i = 0; i < bd->numFramesInFlight; i++) SafeRelease(bd->pFrameResources[i]); @@ -814,6 +845,7 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) io.BackendRendererName = "imgui_impl_webgpu"; #endif io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. bd->initInfo = *init_info; bd->wgpuDevice = init_info->Device; @@ -823,8 +855,6 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) bd->numFramesInFlight = init_info->NumFramesInFlight; bd->frameIndex = UINT_MAX; - bd->renderResources.FontTexture = nullptr; - bd->renderResources.FontTextureView = nullptr; bd->renderResources.Sampler = nullptr; bd->renderResources.Uniforms = nullptr; bd->renderResources.CommonBindGroup = nullptr; @@ -863,7 +893,7 @@ void ImGui_ImplWGPU_Shutdown() io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); IM_DELETE(bd); } diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 1da208f00..61d2d23c0 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -13,8 +13,7 @@ // [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'. -// Missing features or Issues: -// [ ] Renderer: Missing texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). +// [X] Renderer: Texture updates support for dynamic font system (ImGuiBackendFlags_RendererHasTextures). // 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. @@ -57,6 +56,9 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURen IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects(); +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. +IMGUI_IMPL_API void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex); + // [BETA] Selected render state data shared with callbacks. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplWGPU_RenderDrawData() call. // (Please open an issue if you feel you need access to more data) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 20377e2aa..9a24bd72f 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -363,7 +363,7 @@ Other changes: - Misc: added extra operators to ImVec4 in IMGUI_DEFINE_MATH_OPERATORS block. (#8510) [@gan74] - Demo: changed default framed item width to use Min(GetFontSize() * 12, GetContentRegionAvail().x * 0.40f). - Backends: - - Backends: DX9/DX10/DX11/DX12, Vulkan, OpenGL2/3, Metal, SDLGPU3, SDLRenderer2/3, Allegro5: + - Backends: DX9/DX10/DX11/DX12, Vulkan, OpenGL2/3, Metal, SDLGPU3, SDLRenderer2/3, WebGPU, Allegro5: - Added ImGuiBackendFlags_RendererHasTextures support. (#8465, #3761, #3471) [@ocornut, @ShironekoBen, @thedmd] - Added ImGui_ImplXXXX_UpdateTexture(ImTextureData* tex) functions for all backend. From b7f13df130354c1cb7eba3d83aa697b8f489fa32 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 15:42:51 +0200 Subject: [PATCH 06/11] Docs: reformat Changelog. --- docs/CHANGELOG.txt | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9a24bd72f..1cb16ad61 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -362,7 +362,7 @@ Other changes: requires providing a window to the backend. (#8584, #6341) - Misc: added extra operators to ImVec4 in IMGUI_DEFINE_MATH_OPERATORS block. (#8510) [@gan74] - Demo: changed default framed item width to use Min(GetFontSize() * 12, GetContentRegionAvail().x * 0.40f). -- Backends: +- Renderer Backends: - Backends: DX9/DX10/DX11/DX12, Vulkan, OpenGL2/3, Metal, SDLGPU3, SDLRenderer2/3, WebGPU, Allegro5: - Added ImGuiBackendFlags_RendererHasTextures support. (#8465, #3761, #3471) [@ocornut, @ShironekoBen, @thedmd] @@ -370,24 +370,6 @@ Other changes: Available if you want to start uploading textures right after ImGui::Render() and without waiting for the call to ImGui_ImplXXXX_RenderDrawData(). Also useful if you use a staged or multi-threaded rendering schemes, where you might want to set ImDrawData::Textures = NULL. (#8597, #1860) - - Backends: GLFW: added ImGui_ImplGlfw_GetContentScaleForMonitor(), ImGui_ImplGlfw_GetContentScaleForWindow() - helpers. They are wrappers to glfwGetMonitorContentScale()/glfwGetWindowContentScale(), with compile-time - GLFW version checks + returning 1.0f on Apple platform. - - Backends: SDL2: added ImGui_ImplSDL2_GetDpiScaleForDisplay() and ImGui_ImplSDL2_GetContentScaleForWindow() - helpers. They are wrappers to SDL_GetDisplayDPI(), with compile-time SDL version checks + returning 1.0f - on Apple platforms. SDL3 already does this by default. - - Backends: Win32: Fixed an issue where externally losing mouse capture (due to e.g. focus loss) - would fail to claim it again the next subsequent click. (#8594) - - Backends: SDL2, SDL3, OSX: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad - regardless of ImGuiConfigFlags_NavEnableGamepad being set. (#8508) - - Backends: SDL2, SDL3: don't attempt to call SDL_CaptureMouse() on drivers where we don't - call SDL_GetGlobalMouseState(). This is specifically for Wayland but we currently use - the same white-list as SDL_GetGlobalMouseState(). (#8561) [@vs49688] - - Backends: GLFW, SDL2, SDL3: include GLFW/SDL version number in io.BackendPlatformName. - - Backends: SDL3: Update for SDL3 api changes: revert SDL_GetClipboardText() - memory ownership change. (#8530, #7801) [@Green-Sky] - - Backends: SDL3: honor ImGuiPlatformImeData->WantTextInput as an alternative - way to call SDL_StartTextInput(), without IME being necessarily visible. (#8584) - Backends: SDLGPU3: Fixed creating atlas texture earlier than other backends, preventing to load fonts between the Init and NewFrames calls. - Backends: SDLGPU3: Made ImGui_ImplSDLGPU3_PrepareDrawData() reuse GPU Transfer Buffers which @@ -406,6 +388,25 @@ Other changes: - Backends: Vulkan: fixed validation errors in window create/resize helpers used by examples and by multi-viewports implementation, which would typically trigger errors while detaching secondary viewports. (#8600, #8176) [@ChrisTom-94] +- Platform Backends: + - Backends: GLFW: added ImGui_ImplGlfw_GetContentScaleForMonitor(), ImGui_ImplGlfw_GetContentScaleForWindow() + helpers. They are wrappers to glfwGetMonitorContentScale()/glfwGetWindowContentScale(), with compile-time + GLFW version checks + returning 1.0f on Apple platform. + - Backends: SDL2: added ImGui_ImplSDL2_GetDpiScaleForDisplay() and ImGui_ImplSDL2_GetContentScaleForWindow() + helpers. They are wrappers to SDL_GetDisplayDPI(), with compile-time SDL version checks + returning 1.0f + on Apple platforms. SDL3 already does this by default. + - Backends: Win32: Fixed an issue where externally losing mouse capture (due to e.g. focus loss) + would fail to claim it again the next subsequent click. (#8594) + - Backends: SDL2, SDL3, OSX: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad + regardless of ImGuiConfigFlags_NavEnableGamepad being set. (#8508) + - Backends: SDL2, SDL3: don't attempt to call SDL_CaptureMouse() on drivers where we don't + call SDL_GetGlobalMouseState(). This is specifically for Wayland but we currently use + the same white-list as SDL_GetGlobalMouseState(). (#8561) [@vs49688] + - Backends: GLFW, SDL2, SDL3: include GLFW/SDL version number in io.BackendPlatformName. + - Backends: SDL3: Update for SDL3 api changes: revert SDL_GetClipboardText() + memory ownership change. (#8530, #7801) [@Green-Sky] + - Backends: SDL3: honor ImGuiPlatformImeData->WantTextInput as an alternative + way to call SDL_StartTextInput(), without IME being necessarily visible. (#8584) - Examples: - Examples: Made many examples DPI aware by default. The single-viewport is basically: From 7ac99a43662b9dd011610fca500665279af1fbe3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 15:43:12 +0200 Subject: [PATCH 07/11] Backends: OSX: ImGui_ImplOSX_HandleEvent() only process event for window containing our view. (#8644) --- backends/imgui_impl_osx.mm | 4 ++++ docs/CHANGELOG.txt | 2 ++ 2 files changed, 6 insertions(+) diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 32e562472..27567fb19 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -31,6 +31,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-06-12: ImGui_ImplOSX_HandleEvent() only process event for window containing our view. (#8644) // 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set. // 2025-01-20: Removed notification observer when shutting down. (#8331) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: @@ -663,6 +664,9 @@ static ImGuiMouseSource GetMouseSource(NSEvent* event) static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { + // Only process events from the window containing ImGui view + if (event.window != view.window) + return false; ImGuiIO& io = ImGui::GetIO(); if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1cb16ad61..4d73a2aa2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -407,6 +407,8 @@ Other changes: memory ownership change. (#8530, #7801) [@Green-Sky] - Backends: SDL3: honor ImGuiPlatformImeData->WantTextInput as an alternative way to call SDL_StartTextInput(), without IME being necessarily visible. (#8584) + - Backends: OSX: ImGui_ImplOSX_HandleEvent() only process event for window containing + our view. (#8644) [@BingoXuan] - Examples: - Examples: Made many examples DPI aware by default. The single-viewport is basically: From 1ec1510ef7ac64cae4a1876fe25736d05bda02ba Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 12 Jun 2025 16:35:51 +0200 Subject: [PATCH 08/11] Fonts: clarify assert. (#8680) --- docs/CHANGELOG.txt | 5 ++++- imgui.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4d73a2aa2..87dfe5672 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,11 +41,14 @@ HOW TO UPDATE? THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015! I TRIED REALLY HARD TO KEEP THEM TO A MINIMUM, REDUCE THE AMOUNT OF INTERFERENCES, -BUT INEVITABLY SOME USERS WILL BE AFFECTED. +BUT INEVITABLY SOME USERS OR THIRD-PARTY EXTENSIONS WILL BE AFFECTED. IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS, PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO: https://github.com/ocornut/imgui/issues/ +If you are using custom widgets, internals or third-party extension that are somehow +breaking and aren't obvious how to solve, please post in Issues so we can gather +data and share solutions that may help others. As part of the plan to reduce impact of API breaking changes, several unfinished changes/features/refactors related to font and text systems and scaling will be diff --git a/imgui.cpp b/imgui.cpp index f7d2e7441..67e29e228 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5301,8 +5301,12 @@ static void ImGui::UpdateTexturesNewFrame() } else { - IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1 && "If you manage font atlases yourself you need to call ImFontAtlasUpdateNewFrame() on it."); - IM_ASSERT(atlas->RendererHasTextures == has_textures && "If you manage font atlases yourself make sure ImGuiBackendFlags_RendererHasTextures is set consistently with atlas->RendererHasTextures as specified in the ImFontAtlasUpdateNewFrame() call."); + // (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it. + // Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context. + // (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that. + // (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures. + IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1); + IM_ASSERT(atlas->RendererHasTextures == has_textures); } } } @@ -16967,7 +16971,7 @@ void ImGui::DebugNodeFont(ImFont* font) if (baked->ContainerFont != font) continue; PushID(baked_n); - if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.1f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : "")) + if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : "")) { if (SmallButton("Load all")) for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++) From ca3169310ea3e2e8c4a140fd7eaa872edcc2245e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 13 Jun 2025 16:43:58 +0200 Subject: [PATCH 09/11] Fonts: fixed FontBaked=NULL in initial call to SetCurrentWindow() in Begin() using previous frame value of SkipItems. (#8465) ref 0e769c5 --- imgui.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/imgui.cpp b/imgui.cpp index 67e29e228..3618384fb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4475,12 +4475,15 @@ static void SetCurrentWindow(ImGuiWindow* window) g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking if (window) { + bool backup_skip_items = window->SkipItems; + window->SkipItems = false; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) { ImGuiViewport* viewport = window->Viewport; g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity() } ImGui::UpdateCurrentFontSize(0.0f); + window->SkipItems = backup_skip_items; ImGui::NavUpdateCurrentWindowIsScrollPushableX(); } } @@ -8695,6 +8698,21 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // Most of the relevant font logic is in imgui_draw.cpp. // Those are high-level support functions. //----------------------------------------------------------------------------- +// - UpdateFontsNewFrame() [Internal] +// - UpdateFontsEndFrame() [Internal] +// - GetDefaultFont() [Internal] +// - RegisterUserTexture() [Internal] +// - UnregisterUserTexture() [Internal] +// - RegisterFontAtlas() [Internal] +// - UnregisterFontAtlas() [Internal] +// - SetCurrentFont() [Internal] +// - UpdateCurrentFontSize() [Internal] +// - SetFontRasterizerDensity() [Internal] +// - PushFont() +// - PopFont() +// - PushFontSize() +// - PopFontSize() +//----------------------------------------------------------------------------- void ImGui::UpdateFontsNewFrame() { From d8da97f756d8e69ed3ebd858c109edf608cebccc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 13 Jun 2025 16:47:23 +0200 Subject: [PATCH 10/11] Fonts: UpdateCurrentFontSize() early out doesn't need to clear FontBaked. This was meant when the code would be lower in the function (after updating e.g. g.FontSize) Amend 0e769c5. --- imgui.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3618384fb..2bd7cebc5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8825,12 +8825,10 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) // Early out to avoid hidden window keeping bakes referenced and out of GC reach. // However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching, so for now we null it. + // FIXME: perhaps g.FontSize should be updated? if (window != NULL && window->SkipItems) if (g.CurrentTable == NULL || g.CurrentTable->CurrentColumn != -1) // See 8465#issuecomment-2951509561. Ideally the SkipItems=true in tables would be amended with extra data. - { - g.FontBaked = NULL; return; - } // Restoring is pretty much only used by PopFont()/PopFontSize() float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f; From cfa43e721aa1300b17d633f7590516587ff23617 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 13 Jun 2025 17:40:17 +0200 Subject: [PATCH 11/11] Windows: clicking on a window close button doesn't claim focus and bring to front. (#8683) Added ImGuiItemFlags_NoFocus, ImGuiButtonFlags_NoFocus. Neither are well specified so marking as experimental. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 ++++ imgui_internal.h | 2 ++ imgui_widgets.cpp | 6 ++++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 87dfe5672..c23a2b7e2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -302,6 +302,7 @@ Other changes: codepath that preserve last contents size when collapsed, resulting in programmatically uncollapsing auto-sizing windows having them flicker size for a frame. (#7691) [@achabense] +- Windows: clicking on a window close button doesn't claim focus and bring to front. (#8683) - Windows: loosened code to allow hovering of resize grips, borders, and table borders while hovering a sibling child window, so that the code in master matches one in docking (they accidentally diverged). (#8554) diff --git a/imgui.cpp b/imgui.cpp index 2bd7cebc5..9ed61ba5d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7126,8 +7126,12 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Close button if (has_close_button) + { + g.CurrentItemFlags |= ImGuiItemFlags_NoFocus; if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) *p_open = false; + g.CurrentItemFlags &= ~ImGuiItemFlags_NoFocus; + } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; g.CurrentItemFlags = item_flags_backup; diff --git a/imgui_internal.h b/imgui_internal.h index 1d572d5a6..fe71e49b0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -946,6 +946,7 @@ enum ImGuiItemFlagsPrivate_ ImGuiItemFlags_AllowOverlap = 1 << 14, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. ImGuiItemFlags_NoNavDisableMouseHover = 1 << 15, // false // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false). ImGuiItemFlags_NoMarkEdited = 1 << 16, // false // Skip calling MarkItemEdited() + ImGuiItemFlags_NoFocus = 1 << 17, // false // [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags_NoFocus + ImGuiButtonFlags_NoNavFocus in ButtonBehavior(). // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 20, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. @@ -1023,6 +1024,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) + ImGuiButtonFlags_NoFocus = 1 << 22, // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking. ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, }; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ba35e531b..2e80564ea 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -548,6 +548,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags); if (flags & ImGuiButtonFlags_AllowOverlap) item_flags |= ImGuiItemFlags_AllowOverlap; + if (item_flags & ImGuiItemFlags_NoFocus) + flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus; // Default only reacts to left mouse button if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0) @@ -623,7 +625,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - else + else if (!(flags & ImGuiButtonFlags_NoFocus)) { FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child } @@ -641,7 +643,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool SetFocusID(id, window); FocusWindow(window); } - else + else if (!(flags & ImGuiButtonFlags_NoFocus)) { FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child }