Browse Source

Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_osx.mm
#	backends/imgui_impl_wgpu.cpp
#	backends/imgui_impl_wgpu.h
ocornut 1 month ago
parent
commit
4cf85ee543

+ 1 - 1
.github/ISSUE_TEMPLATE/issue_template.yml

@@ -4,7 +4,7 @@ body:
   - type: markdown
   - type: markdown
     attributes:
     attributes:
       value: |
       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.
         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.
         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.

+ 4 - 0
backends/imgui_impl_osx.mm

@@ -35,6 +35,7 @@
 // CHANGELOG
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 // (minor and older changes stripped away, please see git history for details)
 //  2025-XX-XX: Added support for multiple windows via the ImGuiPlatformIO interface.
 //  2025-XX-XX: Added support for multiple windows via the ImGuiPlatformIO interface.
+//  2025-06-12: ImGui_ImplOSX_HandleEvent() only process event for window containing our view. (#8644)
 //  2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors.
 //  2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors.
 //  2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set.
 //  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)
 //  2025-01-20: Removed notification observer when shutting down. (#8331)
@@ -697,6 +698,9 @@ static ImGuiMouseSource GetMouseSource(NSEvent* event)
 
 
 static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
 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();
     ImGuiIO& io = ImGui::GetIO();
 
 
     if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown)
     if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown)

+ 93 - 65
backends/imgui_impl_wgpu.cpp

@@ -6,8 +6,8 @@
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef!
 //  [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: 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: 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).
 // Missing features or Issues:
 // Missing features or Issues:
-//  [ ] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
 //  [ ] Renderer: Multi-viewport support (multiple windows), useful for desktop.
 //  [ ] Renderer: Multi-viewport support (multiple windows), useful for desktop.
 
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -20,6 +20,7 @@
 
 
 // CHANGELOG
 // CHANGELOG
 // (minor and older changes stripped away, please see git history for details)
 // (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.
 //  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-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.
 //  2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
@@ -74,11 +75,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).
 #define MEMALIGN(_SIZE,_ALIGN)        (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))    // Memory align (copied from IM_ALIGN() macro).
 
 
 // WebGPU data
 // WebGPU data
+struct ImGui_ImplWGPU_Texture
+{
+    WGPUTexture         Texture = nullptr;
+    WGPUTextureView     TextureView = nullptr;
+};
+
 struct RenderResources
 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
     WGPUBuffer          Uniforms = nullptr;             // Shader uniforms
     WGPUBindGroup       CommonBindGroup = nullptr;      // Resources bind-group to bind the common resources to pipeline
     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)
     ImGuiStorage        ImageBindGroups;                // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map)
@@ -235,23 +240,8 @@ static void SafeRelease(WGPUShaderModule& res)
         wgpuShaderModuleRelease(res);
         wgpuShaderModuleRelease(res);
     res = nullptr;
     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)
 static void SafeRelease(RenderResources& res)
 {
 {
-    SafeRelease(res.FontTexture);
-    SafeRelease(res.FontTextureView);
     SafeRelease(res.Sampler);
     SafeRelease(res.Sampler);
     SafeRelease(res.Uniforms);
     SafeRelease(res.Uniforms);
     SafeRelease(res.CommonBindGroup);
     SafeRelease(res.CommonBindGroup);
@@ -382,6 +372,13 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
     if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0)
     if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0)
         return;
         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!
     // 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.
     // 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();
     ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();
@@ -537,33 +534,52 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
     platform_io.Renderer_RenderState = nullptr;
     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;
+
+    IM_ASSERT(backend_tex->TextureView == (WGPUTextureView)(intptr_t)tex->TexID);
+    wgpuTextureViewRelease(backend_tex->TextureView);
+    wgpuTextureRelease(backend_tex->Texture);
+    IM_DELETE(backend_tex);
 
 
-    // Upload texture to graphics system
+    // 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 = {};
         WGPUTextureDescriptor tex_desc = {};
 #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
 #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
 #else
-        tex_desc.label = "Dear ImGui Font Texture";
+        tex_desc.label = "Dear ImGui Texture";
 #endif
 #endif
         tex_desc.dimension = WGPUTextureDimension_2D;
         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.size.depthOrArrayLayers = 1;
         tex_desc.sampleCount = 1;
         tex_desc.sampleCount = 1;
         tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
         tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
         tex_desc.mipLevelCount = 1;
         tex_desc.mipLevelCount = 1;
         tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding;
         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 = {};
         WGPUTextureViewDescriptor tex_view_desc = {};
         tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm;
         tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm;
         tex_view_desc.dimension = WGPUTextureViewDimension_2D;
         tex_view_desc.dimension = WGPUTextureViewDimension_2D;
@@ -572,19 +588,35 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
         tex_view_desc.baseArrayLayer = 0;
         tex_view_desc.baseArrayLayer = 0;
         tex_view_desc.arrayLayerCount = 1;
         tex_view_desc.arrayLayerCount = 1;
         tex_view_desc.aspect = WGPUTextureAspect_All;
         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)
 #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
         WGPUTexelCopyTextureInfo dst_view = {};
         WGPUTexelCopyTextureInfo dst_view = {};
 #else
 #else
         WGPUImageCopyTexture dst_view = {};
         WGPUImageCopyTexture dst_view = {};
 #endif
 #endif
-        dst_view.texture = bd->renderResources.FontTexture;
+        dst_view.texture = backend_tex->Texture;
         dst_view.mipLevel = 0;
         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;
         dst_view.aspect = WGPUTextureAspect_All;
 #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
 #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
         WGPUTexelCopyBufferLayout layout = {};
         WGPUTexelCopyBufferLayout layout = {};
@@ -592,29 +624,14 @@ static void ImGui_ImplWGPU_CreateFontsTexture()
         WGPUTextureDataLayout layout = {};
         WGPUTextureDataLayout layout = {};
 #endif
 #endif
         layout.offset = 0;
         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);
     }
     }
-
-    // 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);
+    if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
+        ImGui_ImplWGPU_DestroyTexture(tex);
 }
 }
 
 
 static void ImGui_ImplWGPU_CreateUniformBuffer()
 static void ImGui_ImplWGPU_CreateUniformBuffer()
@@ -758,16 +775,26 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
 
 
     bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc);
     bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc);
 
 
-    ImGui_ImplWGPU_CreateFontsTexture();
     ImGui_ImplWGPU_CreateUniformBuffer();
     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
     // Create resource bind group
     WGPUBindGroupEntry common_bg_entries[] =
     WGPUBindGroupEntry common_bg_entries[] =
     {
     {
         { nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 },
         { nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 },
         { nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 },
         { nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 },
     };
     };
-
     WGPUBindGroupDescriptor common_bg_descriptor = {};
     WGPUBindGroupDescriptor common_bg_descriptor = {};
     common_bg_descriptor.layout = bg_layouts[0];
     common_bg_descriptor.layout = bg_layouts[0];
     common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry);
     common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry);
@@ -792,8 +819,10 @@ void ImGui_ImplWGPU_InvalidateDeviceObjects()
     SafeRelease(bd->pipelineState);
     SafeRelease(bd->pipelineState);
     SafeRelease(bd->renderResources);
     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++)
     for (unsigned int i = 0; i < bd->numFramesInFlight; i++)
         SafeRelease(bd->pFrameResources[i]);
         SafeRelease(bd->pFrameResources[i]);
@@ -818,6 +847,7 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
     io.BackendRendererName = "imgui_impl_webgpu";
     io.BackendRendererName = "imgui_impl_webgpu";
 #endif
 #endif
     io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
     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->initInfo = *init_info;
     bd->wgpuDevice = init_info->Device;
     bd->wgpuDevice = init_info->Device;
@@ -827,8 +857,6 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
     bd->numFramesInFlight = init_info->NumFramesInFlight;
     bd->numFramesInFlight = init_info->NumFramesInFlight;
     bd->frameIndex = UINT_MAX;
     bd->frameIndex = UINT_MAX;
 
 
-    bd->renderResources.FontTexture = nullptr;
-    bd->renderResources.FontTextureView = nullptr;
     bd->renderResources.Sampler = nullptr;
     bd->renderResources.Sampler = nullptr;
     bd->renderResources.Uniforms = nullptr;
     bd->renderResources.Uniforms = nullptr;
     bd->renderResources.CommonBindGroup = nullptr;
     bd->renderResources.CommonBindGroup = nullptr;
@@ -867,7 +895,7 @@ void ImGui_ImplWGPU_Shutdown()
 
 
     io.BackendRendererName = nullptr;
     io.BackendRendererName = nullptr;
     io.BackendRendererUserData = nullptr;
     io.BackendRendererUserData = nullptr;
-    io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
+    io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
     IM_DELETE(bd);
     IM_DELETE(bd);
 }
 }
 
 

+ 4 - 1
backends/imgui_impl_wgpu.h

@@ -13,8 +13,8 @@
 //  [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID/ImTextureRef!
 //  [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: 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: 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).
 // Missing features or Issues:
 // Missing features or Issues:
-//  [ ] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
 //  [ ] Renderer: Multi-viewport support (multiple windows), useful for desktop.
 //  [ ] Renderer: Multi-viewport support (multiple windows), useful for desktop.
 
 
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
 // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
@@ -58,6 +58,9 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURen
 IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
 IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();
 IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
 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.
 // [BETA] Selected render state data shared with callbacks.
 // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplWGPU_RenderDrawData() call.
 // 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)
 // (Please open an issue if you feel you need access to more data)

+ 30 - 23
docs/CHANGELOG.txt

@@ -41,11 +41,14 @@ HOW TO UPDATE?
 
 
 THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015!
 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,
 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,
 IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS,
 PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO:
 PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO:
     https://github.com/ocornut/imgui/issues/
     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
 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
 changes/features/refactors related to font and text systems and scaling will be
@@ -115,8 +118,8 @@ Breaking changes:
   to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this)
   to 4096 but that limit isn't necessary anymore, and Renderer_TextureMaxWidth covers this)
   However you may set TexMinWidth = TexMaxWidth for the same effect.
   However you may set TexMinWidth = TexMaxWidth for the same effect.
 - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on
 - 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
 - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using
   PushFontSize(style.FontSizeBase * factor) or to manipulate other scaling factors.
   PushFontSize(style.FontSizeBase * factor) or to manipulate other scaling factors.
 - Fonts: obsoleted ImFont::Scale which is not useful anymore.
 - Fonts: obsoleted ImFont::Scale which is not useful anymore.
@@ -302,6 +305,7 @@ Other changes:
   codepath that preserve last contents size when collapsed, resulting in
   codepath that preserve last contents size when collapsed, resulting in
   programmatically uncollapsing auto-sizing windows having them flicker size
   programmatically uncollapsing auto-sizing windows having them flicker size
   for a frame. (#7691) [@achabense]
   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
 - 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
   borders while hovering a sibling child window, so that the code in master matches
   one in docking (they accidentally diverged). (#8554)
   one in docking (they accidentally diverged). (#8554)
@@ -365,32 +369,14 @@ Other changes:
   requires providing a window to the backend. (#8584, #6341)
   requires providing a window to the backend. (#8584, #6341)
 - Misc: added extra operators to ImVec4 in IMGUI_DEFINE_MATH_OPERATORS block. (#8510) [@gan74]
 - 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).
 - 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:
+- Renderer Backends:
+  - Backends: DX9/DX10/DX11/DX12, Vulkan, OpenGL2/3, Metal, SDLGPU3, SDLRenderer2/3, WebGPU, Allegro5:
     - Added ImGuiBackendFlags_RendererHasTextures support. (#8465, #3761, #3471)
     - Added ImGuiBackendFlags_RendererHasTextures support. (#8465, #3761, #3471)
       [@ocornut, @ShironekoBen, @thedmd]
       [@ocornut, @ShironekoBen, @thedmd]
     - Added ImGui_ImplXXXX_UpdateTexture(ImTextureData* tex) functions for all backend.
     - Added ImGui_ImplXXXX_UpdateTexture(ImTextureData* tex) functions for all backend.
       Available if you want to start uploading textures right after ImGui::Render() and without
       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
       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)
       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
   - Backends: SDLGPU3: Fixed creating atlas texture earlier than other backends, preventing
     to load fonts between the Init and NewFrames calls.
     to load fonts between the Init and NewFrames calls.
   - Backends: SDLGPU3: Made ImGui_ImplSDLGPU3_PrepareDrawData() reuse GPU Transfer Buffers which
   - Backends: SDLGPU3: Made ImGui_ImplSDLGPU3_PrepareDrawData() reuse GPU Transfer Buffers which
@@ -409,6 +395,27 @@ Other changes:
   - Backends: Vulkan: fixed validation errors in window create/resize helpers used by examples
   - 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
     and by multi-viewports implementation, which would typically trigger errors while detaching
     secondary viewports. (#8600, #8176) [@ChrisTom-94]
     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)
+  - Backends: OSX: ImGui_ImplOSX_HandleEvent() only process event for window containing
+    our view. (#8644) [@BingoXuan]
 - Examples:
 - Examples:
   - Examples: Made many examples DPI aware by default.
   - Examples: Made many examples DPI aware by default.
     The single-viewport is basically:
     The single-viewport is basically:

+ 2 - 1
docs/FONTS.md

@@ -105,6 +105,7 @@ io.Fonts->AddFontDefault();
 ```
 ```
 
 
 **Load .TTF/.OTF file with:**
 **Load .TTF/.OTF file with:**
+
 🆕 **Since 1.92, with an up to date backend: passing a size is not necessary**
 🆕 **Since 1.92, with an up to date backend: passing a size is not necessary**
 ```cpp
 ```cpp
 ImGuiIO& io = ImGui::GetIO();
 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).
 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)**
 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)**
 
 

+ 55 - 21
imgui.cpp

@@ -21,9 +21,10 @@
 // - Issues & support ........... https://github.com/ocornut/imgui/issues
 // - 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)
 // - 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.
 // 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.
 // 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
 // Copyright (c) 2014-2025 Omar Cornut
 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
 // Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
@@ -348,12 +349,12 @@ CODE
         ImGui::Render();
         ImGui::Render();
 
 
         // Update textures
         // Update textures
-        for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
+        ImDrawData* draw_data = ImGui::GetDrawData();
+        for (ImTextureData* tex : *draw_data->Textures)
             if (tex->Status != ImTextureStatus_OK)
             if (tex->Status != ImTextureStatus_OK)
                 MyImGuiBackend_UpdateTexture(tex);
                 MyImGuiBackend_UpdateTexture(tex);
 
 
         // Render dear imgui contents, swap buffers
         // Render dear imgui contents, swap buffers
-        ImDrawData* draw_data = ImGui::GetDrawData();
         MyImGuiBackend_RenderDrawData(draw_data);
         MyImGuiBackend_RenderDrawData(draw_data);
         SwapBuffers();
         SwapBuffers();
      }
      }
@@ -373,25 +374,32 @@ CODE
     {
     {
         if (tex->Status == ImTextureStatus_WantCreate)
         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;
+            // <create texture based on tex->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)
         if (tex->Status == ImTextureStatus_WantUpdates)
         {
         {
-            // update texture blocks based on tex->UpdateRect
-            // tex->Status = ImTextureStatus_OK;
+            // <update texture blocks based on tex->UpdateRect>
+            tex->SetStatus(ImTextureStatus_OK);
         }
         }
         if (tex->Status == ImTextureStatus_WantDestroy)
         if (tex->Status == ImTextureStatus_WantDestroy)
         {
         {
-            // destroy texture
-            // call tex->SetTexID(ImTextureID_Invalid)
-            // tex->Status = ImTextureStatus_Destroyed;
+            // <destroy texture>
+            tex->SetTexID(ImTextureID_Invalid);
+            tex->SetStatus(ImTextureStatus_Destroyed);
         }
         }
     }
     }
 
 
     void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data)
     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 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 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
        // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize
@@ -408,7 +416,10 @@ CODE
              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
              const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
              if (pcmd->UserCallback)
              if (pcmd->UserCallback)
              {
              {
-                 pcmd->UserCallback(cmd_list, pcmd);
+                 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
+                     MyEngineResetRenderState();
+                 else
+                     pcmd->UserCallback(cmd_list, pcmd);
              }
              }
              else
              else
              {
              {
@@ -480,7 +491,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: 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: 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: 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: 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: 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:
                        - 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:
@@ -4567,12 +4578,15 @@ static void SetCurrentWindow(ImGuiWindow* window)
     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
     g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
     if (window)
     if (window)
     {
     {
+        bool backup_skip_items = window->SkipItems;
+        window->SkipItems = false;
         if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
         if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
         {
         {
             ImGuiViewport* viewport = window->Viewport;
             ImGuiViewport* viewport = window->Viewport;
             g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity()
             g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity()
         }
         }
         ImGui::UpdateCurrentFontSize(0.0f);
         ImGui::UpdateCurrentFontSize(0.0f);
+        window->SkipItems = backup_skip_items;
         ImGui::NavUpdateCurrentWindowIsScrollPushableX();
         ImGui::NavUpdateCurrentWindowIsScrollPushableX();
     }
     }
 }
 }
@@ -5467,13 +5481,16 @@ static void ImGui::UpdateTexturesNewFrame()
     {
     {
         if (atlas->OwnerContext == &g)
         if (atlas->OwnerContext == &g)
         {
         {
-            atlas->RendererHasTextures = has_textures;
-            ImFontAtlasUpdateNewFrame(atlas, g.FrameCount);
+            ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures);
         }
         }
         else
         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.");
+            // (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);
         }
         }
     }
     }
 }
 }
@@ -7482,8 +7499,12 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
 
 
     // Close button
     // Close button
     if (has_close_button)
     if (has_close_button)
+    {
+        g.CurrentItemFlags |= ImGuiItemFlags_NoFocus;
         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
         if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
             *p_open = false;
             *p_open = false;
+        g.CurrentItemFlags &= ~ImGuiItemFlags_NoFocus;
+    }
 
 
     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
     window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
     g.CurrentItemFlags = item_flags_backup;
     g.CurrentItemFlags = item_flags_backup;
@@ -9292,6 +9313,21 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
 // Most of the relevant font logic is in imgui_draw.cpp.
 // Most of the relevant font logic is in imgui_draw.cpp.
 // Those are high-level support functions.
 // 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()
 void ImGui::UpdateFontsNewFrame()
 {
 {
@@ -9404,12 +9440,10 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)
 
 
     // Early out to avoid hidden window keeping bakes referenced and out of GC reach.
     // 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.
     // 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 (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.
         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;
             return;
-        }
 
 
     // Restoring is pretty much only used by PopFont()/PopFontSize()
     // 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;
     float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f;
@@ -22799,7 +22833,7 @@ void ImGui::DebugNodeFont(ImFont* font)
         if (baked->ContainerFont != font)
         if (baked->ContainerFont != font)
             continue;
             continue;
         PushID(baked_n);
         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"))
             if (SmallButton("Load all"))
                 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)
                 for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)

+ 6 - 5
imgui_draw.cpp

@@ -2722,12 +2722,13 @@ static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* at
 }
 }
 
 
 // Called by NewFrame() for atlases owned by a context.
 // 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
     // Check that font atlas was built or backend support texture reload in which case we can build now
     if (atlas->RendererHasTextures)
     if (atlas->RendererHasTextures)

+ 3 - 1
imgui_internal.h

@@ -957,6 +957,7 @@ enum ImGuiItemFlagsPrivate_
     ImGuiItemFlags_AllowOverlap             = 1 << 14, // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
     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_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_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
     // 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.
     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.
@@ -1034,6 +1035,7 @@ enum ImGuiButtonFlagsPrivate_
     ImGuiButtonFlags_NoHoveredOnFocus       = 1 << 19,  // don't report as hovered when nav focus is on this item
     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_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_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_PressedOnMask_         = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,
     ImGuiButtonFlags_PressedOnDefault_      = ImGuiButtonFlags_PressedOnClickRelease,
     ImGuiButtonFlags_PressedOnDefault_      = ImGuiButtonFlags_PressedOnClickRelease,
 };
 };
@@ -4131,7 +4133,7 @@ IMGUI_API ImTextureRect*    ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtl
 IMGUI_API ImTextureRect*    ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);
 IMGUI_API ImTextureRect*    ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);
 IMGUI_API void              ImFontAtlasPackDiscardRect(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              ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
 IMGUI_API void              ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
 IMGUI_API void              ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
 IMGUI_API void              ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);
 IMGUI_API void              ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);

+ 4 - 2
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);
     ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
     if (flags & ImGuiButtonFlags_AllowOverlap)
     if (flags & ImGuiButtonFlags_AllowOverlap)
         item_flags |= ImGuiItemFlags_AllowOverlap;
         item_flags |= ImGuiItemFlags_AllowOverlap;
+    if (item_flags & ImGuiItemFlags_NoFocus)
+        flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus;
 
 
     // Default only reacts to left mouse button
     // Default only reacts to left mouse button
     if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
     if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
@@ -623,7 +625,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool
                         SetFocusID(id, window);
                         SetFocusID(id, window);
                         FocusWindow(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
                         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);
                         SetFocusID(id, window);
                         FocusWindow(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
                         FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
                     }
                     }