From db049db860886906c4cbe16b22c4d72748c7a32f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Jan 2024 13:14:21 +0100 Subject: [PATCH 01/11] Docs: tweak, fixed misplaced changelog entry. (#7084) --- docs/CHANGELOG.txt | 5 +++-- docs/README.md | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index dcf12032..6088d006 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -90,6 +90,9 @@ Other changes: - Color Editors: - ColorEdit: Layout tweaks for very small sizes. (#7120, #7121) - ColorPicker: Fixed saturation/value cursor radius not scaling properly. +- Tabs: Added ImGuiTabItemFlags_NoAssumedClosure to enable app to react on closure attempt, + without having to draw an unsaved document marker (ImGuiTabItemFlags_UnsavedDocument sets + _NoAssumedClosure automatically). (#7084) - Debug Tools: - Added io.ConfigDebugIsDebuggerPresent option. When enabled, this adds buttons in various locations of Metrics/Debugger to manually request a debugger break: @@ -3530,8 +3533,6 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Breaking Changes: -- Tabs: Added ImGuiTabItemFlags_NoAssumedClosure to enable app to react on closure attempt, without having to draw - an unsaved document marker (ImGuiTabItemFlags_UnsavedDocument sets _NoAssumedClosure automatically). (#7084) - DragInt(): The default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more. If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, diff --git a/docs/README.md b/docs/README.md index 27467920..ef4fb975 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ Businesses: support continued development and maintenance via invoiced sponsorin | [The Pitch](#the-pitch) - [Usage](#usage) - [How it works](#how-it-works) - [Releases & Changelogs](#releases--changelogs) - [Demo](#demo) - [Integration](#integration) | :----------------------------------------------------------: | | [Gallery](#gallery) - [Support, FAQ](#support-frequently-asked-questions-faq) - [How to help](#how-to-help) - [Sponsors](https://github.com/ocornut/imgui/wiki/Sponsors) - [Credits](#credits) - [License](#license) | -| [Wiki](https://github.com/ocornut/imgui/wiki) - [Languages & frameworks backends/bindings](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | +| [Wiki](https://github.com/ocornut/imgui/wiki) - [Extensions](https://github.com/ocornut/imgui/wiki/Useful-Extensions) - [Languages bindings & frameworks backends](https://github.com/ocornut/imgui/wiki/Bindings) - [Software using Dear ImGui](https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui) - [User quotes](https://github.com/ocornut/imgui/wiki/Quotes) | ### The Pitch @@ -39,7 +39,7 @@ Dear ImGui is particularly suited to integration in game engines (for tooling), ### Usage -**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. +**The core of Dear ImGui is self-contained within a few platform-agnostic files** which you can easily compile in your application/engine. They are all the files in the root folder of the repository (imgui*.cpp, imgui*.h). **No specific build process is required**. You can add the .cpp files into your existing project. **Backends for a variety of graphics API and rendering platforms** are provided in the [backends/](https://github.com/ocornut/imgui/tree/master/backends) folder, along with example applications in the [examples/](https://github.com/ocornut/imgui/tree/master/examples) folder. You may also create your own backend. Anywhere where you can render textured triangles, you can render Dear ImGui. From 29809d72202865f4518db421ba96f19c46b46ab6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Jan 2024 14:37:13 +0100 Subject: [PATCH 02/11] Version 1.90.2 WIP --- docs/CHANGELOG.txt | 8 ++++++++ imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6088d006..5f214721 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,14 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.90.2 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +Other changes: + ----------------------------------------------------------------------- VERSION 1.90.1 (Released 2024-01-10) diff --git a/imgui.cpp b/imgui.cpp index e406a9f1..a8078f1f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 0ec19776..1ee1ac53 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (headers) // Help: @@ -23,8 +23,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.90.1" -#define IMGUI_VERSION_NUM 19010 +#define IMGUI_VERSION "1.90.2 WIP" +#define IMGUI_VERSION_NUM 19011 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 4a116922..53462be7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 40a840c4..9008eb74 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 1a55e171..6f917125 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index eab542d8..272842e0 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e3f420a2..6b16f55b 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.90.1 +// dear imgui, v1.90.2 WIP // (widgets code) /* From 82df7c8bf41b3582996c86251c0dbdc27f651ed0 Mon Sep 17 00:00:00 2001 From: Tristan Gouge Date: Thu, 11 Jan 2024 14:42:48 +0100 Subject: [PATCH 03/11] Backends: Vulkan: Fixed handling of ImGui_ImplVulkan_InitInfo::MinAllocationSize field. (#7189, #4238) --- backends/imgui_impl_vulkan.cpp | 8 ++++---- docs/CHANGELOG.txt | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 47e6fd82..0648a55c 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -33,6 +33,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2024-01-11: Vulkan: Fixed MinAllocationSize handing. (#7189) // 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238) // 2024-01-03: Vulkan: Stoped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them. // 2023-11-29: Vulkan: Fixed mismatching allocator passed to vkCreateCommandPool() vs vkDestroyCommandPool(). (#7075) @@ -395,7 +396,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory if (buffer_memory != VK_NULL_HANDLE) vkFreeMemory(v->Device, buffer_memory, v->Allocator); - VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / bd->BufferMemoryAlignment + 1) * bd->BufferMemoryAlignment; + VkDeviceSize vertex_buffer_size_aligned = ((IM_MAX(v->MinAllocationSize, new_size) - 1) / bd->BufferMemoryAlignment + 1) * bd->BufferMemoryAlignment; VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = vertex_buffer_size_aligned; @@ -407,17 +408,16 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory VkMemoryRequirements req; vkGetBufferMemoryRequirements(v->Device, buffer, &req); bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; - VkDeviceSize size = IM_MAX(v->MinAllocationSize, req.size); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = size; + alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &buffer_memory); check_vk_result(err); err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); check_vk_result(err); - p_buffer_size = size; + p_buffer_size = vertex_buffer_size_aligned; } static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5f214721..5722c776 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Backends: Vulkan: Fixed handling of ImGui_ImplVulkan_InitInfo::MinAllocationSize field. (#7189, #4238) + ----------------------------------------------------------------------- VERSION 1.90.1 (Released 2024-01-10) From 70bb6d1e7906ac98eef5be5cf7c8b32f240c2968 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Jan 2024 14:57:38 +0100 Subject: [PATCH 04/11] Backends: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size. (#3957) --- backends/imgui_impl_vulkan.cpp | 24 +++++++++++++++--------- docs/CHANGELOG.txt | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 0648a55c..6fb7f16f 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -33,7 +33,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-01-11: Vulkan: Fixed MinAllocationSize handing. (#7189) +// 2024-01-11: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size (#3957). Fixed MinAllocationSize handing (#7189). // 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238) // 2024-01-03: Vulkan: Stoped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them. // 2023-11-29: Vulkan: Fixed mismatching allocator passed to vkCreateCommandPool() vs vkDestroyCommandPool(). (#7075) @@ -386,7 +386,13 @@ static void check_vk_result(VkResult err) v->CheckVkResultFn(err); } -static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& p_buffer_size, size_t new_size, VkBufferUsageFlagBits usage) +// Same as IM_MEMALIGN(). 'alignment' must be a power of two. +static inline VkDeviceSize AlignBufferSize(VkDeviceSize size, VkDeviceSize alignment) +{ + return (size + alignment - 1) & ~(alignment - 1); +} + +static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, size_t new_size, VkBufferUsageFlagBits usage) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; @@ -396,10 +402,10 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory if (buffer_memory != VK_NULL_HANDLE) vkFreeMemory(v->Device, buffer_memory, v->Allocator); - VkDeviceSize vertex_buffer_size_aligned = ((IM_MAX(v->MinAllocationSize, new_size) - 1) / bd->BufferMemoryAlignment + 1) * bd->BufferMemoryAlignment; + VkDeviceSize buffer_size_aligned = AlignBufferSize(IM_MAX(v->MinAllocationSize, new_size), bd->BufferMemoryAlignment); VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = vertex_buffer_size_aligned; + buffer_info.size = buffer_size_aligned; buffer_info.usage = usage; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &buffer); @@ -417,7 +423,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); check_vk_result(err); - p_buffer_size = vertex_buffer_size_aligned; + buffer_size = buffer_size_aligned; } static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) @@ -494,8 +500,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (draw_data->TotalVtxCount > 0) { // Create or resize the vertex/index buffers - size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); - size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); + size_t vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment); + size_t index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment); if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size) CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size) @@ -504,9 +510,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm // Upload vertex/index data into a single contiguous GPU buffer ImDrawVert* vtx_dst = nullptr; ImDrawIdx* idx_dst = nullptr; - VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, rb->VertexBufferSize, 0, (void**)&vtx_dst); + VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)&vtx_dst); check_vk_result(err); - err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, rb->IndexBufferSize, 0, (void**)&idx_dst); + err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)&idx_dst); check_vk_result(err); for (int n = 0; n < draw_data->CmdListsCount; n++) { diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5722c776..90e00d95 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,7 @@ Breaking changes: Other changes: +- Backends: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size. (#3957) - Backends: Vulkan: Fixed handling of ImGui_ImplVulkan_InitInfo::MinAllocationSize field. (#7189, #4238) From 6228c2e1ec7ef21ca1809579c055ed34540dedb0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Jan 2024 20:22:37 +0100 Subject: [PATCH 05/11] Backends: Vulkan: moved ImGui_ImplVulkanH_DestroyFrameRenderBuffers/ImGui_ImplVulkanH_DestroyWindowRenderBuffers as they are always used in a state where backend data is available. --- backends/imgui_impl_vulkan.cpp | 70 +++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 6fb7f16f..73213c98 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -92,14 +92,14 @@ #endif // Forward Declarations -struct ImGui_ImplVulkanH_FrameRenderBuffers; -struct ImGui_ImplVulkanH_WindowRenderBuffers; +struct ImGui_ImplVulkan_FrameRenderBuffers; +struct ImGui_ImplVulkan_WindowRenderBuffers; bool ImGui_ImplVulkan_CreateDeviceObjects(); void ImGui_ImplVulkan_DestroyDeviceObjects(); +void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); -void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); @@ -191,7 +191,7 @@ static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR; // Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() // [Please zero-clear before use!] -struct ImGui_ImplVulkanH_FrameRenderBuffers +struct ImGui_ImplVulkan_FrameRenderBuffers { VkDeviceMemory VertexBufferMemory; VkDeviceMemory IndexBufferMemory; @@ -203,11 +203,11 @@ struct ImGui_ImplVulkanH_FrameRenderBuffers // Each viewport will hold 1 ImGui_ImplVulkanH_WindowRenderBuffers // [Please zero-clear before use!] -struct ImGui_ImplVulkanH_WindowRenderBuffers +struct ImGui_ImplVulkan_WindowRenderBuffers { uint32_t Index; uint32_t Count; - ImGui_ImplVulkanH_FrameRenderBuffers* FrameRenderBuffers; + ImGui_ImplVulkan_FrameRenderBuffers* FrameRenderBuffers; }; // Vulkan data @@ -234,7 +234,7 @@ struct ImGui_ImplVulkan_Data VkCommandBuffer FontCommandBuffer; // Render buffers for main window - ImGui_ImplVulkanH_WindowRenderBuffers MainWindowRenderBuffers; + ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; ImGui_ImplVulkan_Data() { @@ -426,7 +426,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory buffer_size = buffer_size_aligned; } -static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) +static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); @@ -485,17 +485,17 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm pipeline = bd->Pipeline; // Allocate array to store enough vertex/index buffers - ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; + ImGui_ImplVulkan_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; if (wrb->FrameRenderBuffers == nullptr) { wrb->Index = 0; wrb->Count = v->ImageCount; - wrb->FrameRenderBuffers = (ImGui_ImplVulkanH_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameRenderBuffers) * wrb->Count); - memset(wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkanH_FrameRenderBuffers) * wrb->Count); + wrb->FrameRenderBuffers = (ImGui_ImplVulkan_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); + memset(wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); } IM_ASSERT(wrb->Count == v->ImageCount); wrb->Index = (wrb->Index + 1) % wrb->Count; - ImGui_ImplVulkanH_FrameRenderBuffers* rb = &wrb->FrameRenderBuffers[wrb->Index]; + ImGui_ImplVulkan_FrameRenderBuffers* rb = &wrb->FrameRenderBuffers[wrb->Index]; if (draw_data->TotalVtxCount > 0) { @@ -1032,7 +1032,7 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); ImGui_ImplVulkan_DestroyFontsTexture(); if (bd->FontCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->FontCommandPool, 1, &bd->FontCommandBuffer); bd->FontCommandBuffer = VK_NULL_HANDLE; } @@ -1151,7 +1151,7 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err = vkDeviceWaitIdle(v->Device); check_vk_result(err); - ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); bd->VulkanInitInfo.MinImageCount = min_image_count; } @@ -1198,6 +1198,26 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set) vkFreeDescriptorSets(v->Device, v->DescriptorPool, 1, &descriptor_set); } +void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator) +{ + if (buffers->VertexBuffer) { vkDestroyBuffer(device, buffers->VertexBuffer, allocator); buffers->VertexBuffer = VK_NULL_HANDLE; } + if (buffers->VertexBufferMemory) { vkFreeMemory(device, buffers->VertexBufferMemory, allocator); buffers->VertexBufferMemory = VK_NULL_HANDLE; } + if (buffers->IndexBuffer) { vkDestroyBuffer(device, buffers->IndexBuffer, allocator); buffers->IndexBuffer = VK_NULL_HANDLE; } + if (buffers->IndexBufferMemory) { vkFreeMemory(device, buffers->IndexBufferMemory, allocator); buffers->IndexBufferMemory = VK_NULL_HANDLE; } + buffers->VertexBufferSize = 0; + buffers->IndexBufferSize = 0; +} + +void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator) +{ + for (uint32_t n = 0; n < buffers->Count; n++) + ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); + IM_FREE(buffers->FrameRenderBuffers); + buffers->FrameRenderBuffers = nullptr; + buffers->Index = 0; + buffers->Count = 0; +} + //------------------------------------------------------------------------- // Internal / Miscellaneous Vulkan Helpers // (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) @@ -1562,26 +1582,6 @@ void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH fsd->ImageAcquiredSemaphore = fsd->RenderCompleteSemaphore = VK_NULL_HANDLE; } -void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator) -{ - if (buffers->VertexBuffer) { vkDestroyBuffer(device, buffers->VertexBuffer, allocator); buffers->VertexBuffer = VK_NULL_HANDLE; } - if (buffers->VertexBufferMemory) { vkFreeMemory(device, buffers->VertexBufferMemory, allocator); buffers->VertexBufferMemory = VK_NULL_HANDLE; } - if (buffers->IndexBuffer) { vkDestroyBuffer(device, buffers->IndexBuffer, allocator); buffers->IndexBuffer = VK_NULL_HANDLE; } - if (buffers->IndexBufferMemory) { vkFreeMemory(device, buffers->IndexBufferMemory, allocator); buffers->IndexBufferMemory = VK_NULL_HANDLE; } - buffers->VertexBufferSize = 0; - buffers->IndexBufferSize = 0; -} - -void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator) -{ - for (uint32_t n = 0; n < buffers->Count; n++) - ImGui_ImplVulkanH_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); - IM_FREE(buffers->FrameRenderBuffers); - buffers->FrameRenderBuffers = nullptr; - buffers->Index = 0; - buffers->Count = 0; -} - //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE From 8a3dfda8d082caa4eefa75d8a53ca5074b2c651d Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jan 2024 11:20:13 +0100 Subject: [PATCH 06/11] Commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. Amend 3a90dc38 (#2589, #2598, #3108, #3113, #3653, #4642) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 14 ++------------ imgui.h | 6 +----- imgui_internal.h | 6 +++--- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 90e00d95..20de27ac 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,9 @@ HOW TO UPDATE? Breaking changes: +- Commented out ImGuiIO::ImeWindowHandle obsoleted in 1.87 in favor of writing + to 'void* ImGuiViewport::PlatformHandleRaw'. + Other changes: - Backends: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size. (#3957) diff --git a/imgui.cpp b/imgui.cpp index a8078f1f..2d9ea91b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -424,6 +424,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges. - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls. @@ -5028,18 +5029,7 @@ void ImGui::EndFrame() { IMGUI_DEBUG_LOG_IO("[io] Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); ImGuiViewport* viewport = GetMainViewport(); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (viewport->PlatformHandleRaw == NULL && g.IO.ImeWindowHandle != NULL) - { - viewport->PlatformHandleRaw = g.IO.ImeWindowHandle; - g.IO.SetPlatformImeDataFn(viewport, ime_data); - viewport->PlatformHandleRaw = NULL; - } - else -#endif - { - g.IO.SetPlatformImeDataFn(viewport, ime_data); - } + g.IO.SetPlatformImeDataFn(viewport, ime_data); } // Hide implicit/fallback "Debug" window if it hasn't been used diff --git a/imgui.h b/imgui.h index 1ee1ac53..b079473e 100644 --- a/imgui.h +++ b/imgui.h @@ -2187,11 +2187,7 @@ struct ImGuiIO int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. -#endif -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - void* ImeWindowHandle; // = NULL // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. -#else - void* _UnusedPadding; + //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. #endif //------------------------------------------------------------------ diff --git a/imgui_internal.h b/imgui_internal.h index 6f917125..4c9c8479 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1965,8 +1965,8 @@ struct ImGuiContext #endif // Next window/item data - ImGuiID CurrentFocusScopeId; // == g.FocusScopeStack.back() - ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() + ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId. + ImGuiItemFlags CurrentItemFlags; // Value for currently appending items == g.ItemFlagsStack.back() ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) @@ -1993,7 +1993,7 @@ struct ImGuiContext // Gamepad/keyboard Navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavId; // Focused item for navigation - ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set) + ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) From dd0efdc63716297a8393fa20fccc0e032bfc7a27 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jan 2024 17:31:44 +0100 Subject: [PATCH 07/11] Fixed SetKeyboardFocusHere() not working when current nav focus is in different scope. (#7226) Amend 70f2aaff --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 20de27ac..fab54b07 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Breaking changes: Other changes: +- Fixed SetKeyboardFocusHere() not working when current nav focus is in different scope, + regression from 1.90.1 related to code scoping Tab presses to local scope. (#7226) [@bratpilz] - Backends: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size. (#3957) - Backends: Vulkan: Fixed handling of ImGui_ImplVulkan_InitInfo::MinAllocationSize field. (#7189, #4238) diff --git a/imgui.cpp b/imgui.cpp index 2d9ea91b..8db1bbed 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11383,10 +11383,12 @@ void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flag ImGuiContext& g = *GImGui; if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0) + { if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent) return; - if (g.NavFocusScopeId != g.CurrentFocusScopeId) - return; + if (g.NavFocusScopeId != g.CurrentFocusScopeId) + return; + } // - Can always land on an item when using API call. // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item. From 2156db7a07527a758f75e4cd2ce35566409cd6e2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jan 2024 15:57:14 +0100 Subject: [PATCH 08/11] Debug Log: added InputRouting logging. Made GetKeyChordName() use its own buffer. Fixed debug break in SetShortcutRouting(). (#6798, #2637, #456) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 25 ++++++++++++++++++------- imgui.h | 2 +- imgui_internal.h | 10 +++++++--- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fab54b07..39e7b665 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -48,6 +48,8 @@ Other changes: - Fixed SetKeyboardFocusHere() not working when current nav focus is in different scope, regression from 1.90.1 related to code scoping Tab presses to local scope. (#7226) [@bratpilz] +- Debug Tools: Metrics: Fixed debug break in SetShortcutRouting() not handling ImGuiMod_Shortcut redirect. +- Debug Tools: Debug Log: Added "Input Routing" logging. - Backends: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size. (#3957) - Backends: Vulkan: Fixed handling of ImGui_ImplVulkan_InitInfo::MinAllocationSize field. (#7189, #4238) diff --git a/imgui.cpp b/imgui.cpp index 8db1bbed..71f352eb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8146,18 +8146,18 @@ const char* ImGui::GetKeyName(ImGuiKey key) } // ImGuiMod_Shortcut is translated to either Ctrl or Super. -const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size) +const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; if (key_chord & ImGuiMod_Shortcut) key_chord = ConvertShortcutMod(key_chord); - ImFormatString(out_buf, (size_t)out_buf_size, "%s%s%s%s%s", + ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", (key_chord & ImGuiMod_Alt) ? "Alt+" : "", (key_chord & ImGuiMod_Super) ? (g.IO.ConfigMacOSXBehaviors ? "Cmd+" : "Super+") : "", GetKeyName((ImGuiKey)(key_chord & ~ImGuiMod_Mask_))); - return out_buf; + return g.TempKeychordName; } // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) @@ -8224,6 +8224,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex) { routing_entry = &rt->Entries[old_routing_idx]; + routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingNext = ImGuiKeyOwner_None; routing_entry->RoutingNextScore = 255; @@ -8363,7 +8364,7 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used // [DEBUG] Debug break requested by user - if (g.DebugBreakInShortcutRouting == key_chord) + if (g.DebugBreakInShortcutRouting != 0 && g.DebugBreakInShortcutRouting == ConvertShortcutMod(key_chord)) IM_DEBUG_BREAK(); if (flags & ImGuiInputFlags_RouteUnlessBgFocused) @@ -8371,9 +8372,13 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI return false; // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this? if (flags & ImGuiInputFlags_RouteAlways) + { + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> always\n", GetKeyChordName(key_chord), owner_id, flags); return true; + } const int score = CalcRoutingScore(g.CurrentWindow, owner_id, flags); + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> score %d\n", GetKeyChordName(key_chord), owner_id, flags, score); if (score == 255) return false; @@ -8389,6 +8394,8 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI } // Return routing state for CURRENT frame + if (routing_data->RoutingCurr == routing_id) + IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n"); return routing_data->RoutingCurr == routing_id; } @@ -14350,6 +14357,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) { ImGuiDebugAllocInfo* info = &g.DebugAllocInfo; Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); + if (SmallButton("GC now")) { g.GcCompactAll = true; } Text("Recent frames with allocations:"); int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf); for (int n = buf_size - 1; n >= 0; n--) @@ -14437,10 +14445,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; ) { - char key_chord_name[64]; ImGuiKeyRoutingData* routing_data = &rt->Entries[idx]; ImGuiKeyChord key_chord = key | routing_data->Mods; - Text("%s: 0x%08X", GetKeyChordName(key_chord, key_chord_name, IM_ARRAYSIZE(key_chord_name)), routing_data->RoutingCurr); + Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore); DebugLocateItemOnHover(routing_data->RoutingCurr); if (g.IO.ConfigDebugIsDebuggerPresent) { @@ -15096,7 +15103,10 @@ void ImGui::ShowDebugLogWindow(bool* p_open) return; } - CheckboxFlags("All", &g.DebugLogFlags, ImGuiDebugLogFlags_EventMask_); + ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting; + CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags); + SetItemTooltip("(except InputRouting which is spammy)"); + ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId); ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper); ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus); @@ -15104,6 +15114,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); //ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); + ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) { diff --git a/imgui.h b/imgui.h index b079473e..d9d5d689 100644 --- a/imgui.h +++ b/imgui.h @@ -24,7 +24,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.90.2 WIP" -#define IMGUI_VERSION_NUM 19011 +#define IMGUI_VERSION_NUM 19012 #define IMGUI_HAS_TABLE /* diff --git a/imgui_internal.h b/imgui_internal.h index 4c9c8479..e11c7481 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -234,6 +234,7 @@ namespace ImStb #define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -1369,11 +1370,12 @@ struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super. + ImU8 RoutingCurrScore; // [DEBUG] For debug display ImU8 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; - ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; } + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. @@ -1788,8 +1790,9 @@ enum ImGuiDebugLogFlags_ ImGuiDebugLogFlags_EventClipper = 1 << 4, ImGuiDebugLogFlags_EventSelection = 1 << 5, ImGuiDebugLogFlags_EventIO = 1 << 6, + ImGuiDebugLogFlags_EventInputRouting = 1 << 7, - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO, + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine }; @@ -2190,6 +2193,7 @@ struct ImGuiContext int WantCaptureKeyboardNextFrame; // " int WantTextInputNextFrame; ImVector TempBuffer; // Temporary text buffer + char TempKeychordName[64]; ImGuiContext(ImFontAtlas* shared_font_atlas) { @@ -3129,7 +3133,7 @@ namespace ImGui IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key); inline ImGuiKeyData* GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); } - IMGUI_API const char* GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size); + IMGUI_API const char* GetKeyChordName(ImGuiKeyChord key_chord); inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); From 4b20a0217eb487d1b1716eef30ec408b800d254b Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jan 2024 11:44:21 +0100 Subject: [PATCH 09/11] Internals: add window to FocusScopeStack. (#6798) --- imgui.cpp | 21 ++++++++++++++------- imgui_internal.h | 26 ++++++++++++++++---------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 71f352eb..3ef0f9bd 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3075,7 +3075,7 @@ void ImGui::PopStyleColor(int count) ImGuiContext& g = *GImGui; if (g.ColorStack.Size < count) { - IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times: stack underflow."); + IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!"); count = g.ColorStack.Size; } while (count > 0) @@ -3138,7 +3138,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) *pvar = val; return; } - IM_ASSERT_USER_ERROR(0, "Called PushStyleVar() variant with wrong type!"); + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); } void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) @@ -3152,7 +3152,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) *pvar = val; return; } - IM_ASSERT_USER_ERROR(0, "Called PushStyleVar() variant with wrong type!"); + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); } void ImGui::PopStyleVar(int count) @@ -3160,7 +3160,7 @@ void ImGui::PopStyleVar(int count) ImGuiContext& g = *GImGui; if (g.StyleVarStack.Size < count) { - IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times: stack underflow."); + IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!"); count = g.StyleVarStack.Size; } while (count > 0) @@ -7798,16 +7798,23 @@ void ImGui::SetWindowFontScale(float scale) void ImGui::PushFocusScope(ImGuiID id) { ImGuiContext& g = *GImGui; - g.FocusScopeStack.push_back(id); + ImGuiFocusScopeData data; + data.ID = id; + data.WindowID = g.CurrentWindow->ID; + g.FocusScopeStack.push_back(data); g.CurrentFocusScopeId = id; } void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ? + if (g.FocusScopeStack.Size == 0) + { + IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!"); + return; + } g.FocusScopeStack.pop_back(); - g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back() : 0; + g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0; } // Focus = move navigation cursor, set scrolling, set focus window. diff --git a/imgui_internal.h b/imgui_internal.h index e11c7481..2031535c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1571,6 +1571,12 @@ struct ImGuiNavItemData void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; } }; +struct ImGuiFocusScopeData +{ + ImGuiID ID; + ImGuiID WindowID; +}; + //----------------------------------------------------------------------------- // [SECTION] Typing-select support //----------------------------------------------------------------------------- @@ -1977,16 +1983,16 @@ struct ImGuiContext bool DebugShowGroupRects; // Shared stacks - ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) - ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() - ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() - ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() - ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() - ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() - ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() - ImVector OpenPopupStack; // Which popups are open (persistent) - ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) - ImVector NavTreeNodeStack; // Stack for TreeNode() when a NavLeft requested is emitted. + ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) + ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() + ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() + ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() + ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() + ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() + ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() + ImVector OpenPopupStack; // Which popups are open (persistent) + ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + ImVector NavTreeNodeStack; // Stack for TreeNode() when a NavLeft requested is emitted. int BeginMenuCount; From e0c8c80adaadbd25157199dcb5e5eadf84ad6f47 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jan 2024 14:18:10 +0100 Subject: [PATCH 10/11] Shortcut()/SetShortcutRouting(): focus route testing now use ParentWindowForFocusRoute. Automatically set on child-window, manually configurable otherwise. (#6798, #2637, #456) --- imgui.cpp | 26 ++++++++++++++------------ imgui_internal.h | 1 + 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 3ef0f9bd..a6b92d44 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6432,13 +6432,18 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { UpdateWindowParentAndRootLinks(window, flags, parent_window); window->ParentWindowInBeginStack = parent_window_in_stack; + + // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack, + // e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798) + window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL; } // Add to focus scope stack - // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() if ((flags & ImGuiWindowFlags_NavFlattened) == 0) PushFocusScope(window->ID); window->NavRootFocusScopeId = g.CurrentFocusScopeId; + + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindow = NULL; // Add to popup stack @@ -8325,25 +8330,19 @@ static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputF if (owner_id != 0 && g.ActiveId == owner_id) return 1; - // Early out when not in focus stack - if (focused == NULL || focused->RootWindow != location->RootWindow) - return 255; - // Score based on distance to focused window (lower is better) // Assuming both windows are submitting a routing request, // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match) // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best) // Assuming only WindowA is submitting a routing request, // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. - for (int next_score = 3; focused != NULL; next_score++) - { + // FIXME: This could be later abstracted as a focus path + for (int next_score = 3; focused != NULL; next_score++, focused = focused->ParentWindowForFocusRoute) if (focused == location) { IM_ASSERT(next_score < 255); return next_score; } - focused = (focused->RootWindow != focused) ? focused->ParentWindow : NULL; // FIXME: This could be later abstracted as a focus path - } return 255; } @@ -10780,6 +10779,8 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); + //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack; + return is_open; } @@ -14995,9 +14996,10 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } - if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } - if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } + if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } + if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } + if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); } + if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) { for (ImGuiOldColumns& columns : window->ColumnsStorage) diff --git a/imgui_internal.h b/imgui_internal.h index 2031535c..50b3e3d6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2538,6 +2538,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. + ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document) ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) From 46e5f44ec8c2535415e6bc713e16d9baac9de78e Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 15 Jan 2024 16:56:29 +0100 Subject: [PATCH 11/11] Shortcut()/SetShortcutRouting(): use mixed current window focus scope + ParentWindowForFocusRoute. (#6798, #2637, #456) Amend d474836 Begin: tweak clearing of CurrentWindow as FocusWindow() relies on it now. Addded SetWindowParentWindowForFocusRoute() helper. --- imgui.cpp | 76 ++++++++++++++++++++++++++++++++++-------------- imgui_internal.h | 3 ++ 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a6b92d44..e75b7d95 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6439,13 +6439,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // Add to focus scope stack - if ((flags & ImGuiWindowFlags_NavFlattened) == 0) - PushFocusScope(window->ID); + PushFocusScope((flags & ImGuiWindowFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID); window->NavRootFocusScopeId = g.CurrentFocusScopeId; - // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() - g.CurrentWindow = NULL; - // Add to popup stack if (flags & ImGuiWindowFlags_Popup) { @@ -6510,6 +6506,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() + g.CurrentWindow = NULL; + // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) { @@ -7083,8 +7082,7 @@ void ImGui::End() if (window->DC.CurrentColumns) EndColumns(); PopClipRect(); // Inner window clip rectangle - if ((window->Flags & ImGuiWindowFlags_NavFlattened) == 0) - PopFocusScope(); + PopFocusScope(); // Stop logging if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging @@ -7209,7 +7207,7 @@ void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags) g.NavMousePosDirty = true; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavLayer = ImGuiNavLayer_Main; - g.NavFocusScopeId = window ? window->NavRootFocusScopeId : 0; + SetNavFocusScope(window ? window->NavRootFocusScopeId : 0); g.NavIdIsAlive = false; g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; @@ -7822,6 +7820,33 @@ void ImGui::PopFocusScope() g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0; } +void ImGui::SetNavFocusScope(ImGuiID focus_scope_id) +{ + ImGuiContext& g = *GImGui; + g.NavFocusScopeId = focus_scope_id; + g.NavFocusScopePath.resize(0); // Invalidate + if (focus_scope_id == 0) + return; + IM_ASSERT(g.NavWindow != NULL); + + // Store current path (in reverse order) + if (focus_scope_id == g.CurrentFocusScopeId) + { + // Top of focus stack contains local focus scopes inside current window + for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--) + g.NavFocusScopePath.push_back(g.FocusScopeStack.Data[n]); + } + else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId) + g.NavFocusScopePath.push_back({ focus_scope_id, g.NavWindow->ID }); + else + return; + + // Then follow on manually set ParentWindowForFocusRoute field (#6798) + for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute) + g.NavFocusScopePath.push_back({ window->NavRootFocusScopeId, window->ID }); + IM_ASSERT(g.NavFocusScopePath.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3 +} + // Focus = move navigation cursor, set scrolling, set focus window. void ImGui::FocusItem() { @@ -8318,12 +8343,11 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) // - 254: ImGuiInputFlags_RouteGlobalLow // - 255: never route // 'flags' should include an explicit routing policy -static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputFlags flags) +static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { if (flags & ImGuiInputFlags_RouteFocused) { ImGuiContext& g = *GImGui; - ImGuiWindow* focused = g.NavWindow; // ActiveID gets top priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) @@ -8336,13 +8360,12 @@ static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputF // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best) // Assuming only WindowA is submitting a routing request, // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. - // FIXME: This could be later abstracted as a focus path - for (int next_score = 3; focused != NULL; next_score++, focused = focused->ParentWindowForFocusRoute) - if (focused == location) - { - IM_ASSERT(next_score < 255); - return next_score; - } + if (focus_scope_id == 0) + return 255; + for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusScopePath.Size; index_in_focus_path++) + if (g.NavFocusScopePath.Data[index_in_focus_path].ID == focus_scope_id) + return 3 + index_in_focus_path; + return 255; } @@ -8383,7 +8406,7 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI return true; } - const int score = CalcRoutingScore(g.CurrentWindow, owner_id, flags); + const int score = CalcRoutingScore(g.CurrentFocusScopeId, owner_id, flags); IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> score %d\n", GetKeyChordName(key_chord), owner_id, flags, score); if (score == 255) return false; @@ -11095,7 +11118,7 @@ void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; g.NavLayer = nav_layer; - g.NavFocusScopeId = focus_scope_id; + SetNavFocusScope(focus_scope_id); g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; @@ -11117,7 +11140,7 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; g.NavId = id; g.NavLayer = nav_layer; - g.NavFocusScopeId = g.CurrentFocusScopeId; + SetNavFocusScope(g.CurrentFocusScopeId); window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); @@ -11375,6 +11398,7 @@ static void ImGui::NavProcessItem() if (g.NavWindow != window) SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window. g.NavLayer = window->DC.NavLayerCurrent; + SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath g.NavFocusScopeId = g.CurrentFocusScopeId; g.NavIdIsAlive = true; if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData) @@ -11604,7 +11628,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) if (window->Flags & ImGuiWindowFlags_NoNavInputs) { g.NavId = 0; - g.NavFocusScopeId = window->NavRootFocusScopeId; + SetNavFocusScope(window->NavRootFocusScopeId); return; } @@ -11623,7 +11647,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) else { g.NavId = window->NavLastIds[0]; - g.NavFocusScopeId = window->NavRootFocusScopeId; + SetNavFocusScope(window->NavRootFocusScopeId); } } @@ -14507,6 +14531,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); + Text("NavFocusScopePath[] = "); + for (int path_n = g.NavFocusScopePath.Size - 1; path_n >= 0; path_n--) + { + const ImGuiFocusScopeData& focus_scope = g.NavFocusScopePath[path_n]; + SameLine(0.0f, 0.0f); + Text("0x%08X/", focus_scope.ID); + SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name); + } Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); Unindent(); diff --git a/imgui_internal.h b/imgui_internal.h index 50b3e3d6..2d4d4c63 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2003,6 +2003,7 @@ struct ImGuiContext ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavId; // Focused item for navigation ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) + ImVector NavFocusScopePath; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId) ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) @@ -2952,6 +2953,7 @@ namespace ImGui IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); + inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } @@ -3110,6 +3112,7 @@ namespace ImGui IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX(); IMGUI_API void SetNavWindow(ImGuiWindow* window); IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavFocusScope(ImGuiID focus_scope_id); // Focus/Activation // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are