diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index a69c5110c..53db04764 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -42,5 +42,5 @@ jobs: fi cd examples/example_null pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1 - pvs-studio-analyzer analyze --disableLicenseExpirationCheck -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log + pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 506e1731b..ed22e7f08 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -23,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002) // 2025-09-29: DirectX12: Rework synchronization logic. (#8961) // 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965) // 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963) @@ -111,6 +112,9 @@ struct ImGui_ImplDX12_Data ID3D12CommandAllocator* pTexCmdAllocator; ID3D12GraphicsCommandList* pTexCmdList; + ID3D12Resource* pTexUploadBuffer; + UINT pTexUploadBufferSize; + void* pTexUploadBufferMapped; ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -513,44 +517,53 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) UINT upload_pitch_dst = (upload_pitch_src + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); UINT upload_size = upload_pitch_dst * upload_h; - D3D12_RESOURCE_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Alignment = 0; - desc.Width = upload_size; - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; + if (bd->pTexUploadBuffer == nullptr || upload_size > bd->pTexUploadBufferSize) + { + if (bd->pTexUploadBufferMapped) + { + D3D12_RANGE range = { 0, bd->pTexUploadBufferSize }; + bd->pTexUploadBuffer->Unmap(0, &range); + bd->pTexUploadBufferMapped = nullptr; + } + SafeRelease(bd->pTexUploadBuffer); - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + D3D12_RESOURCE_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + desc.Alignment = 0; + desc.Width = upload_size; + desc.Height = 1; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_UNKNOWN; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; - // FIXME-OPT: Could upload buffer be kept around, reused, and grown only when needed? Would that be worth it? - ID3D12Resource* uploadBuffer = nullptr; - HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)); - IM_ASSERT(SUCCEEDED(hr)); + D3D12_HEAP_PROPERTIES props; + memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); + props.Type = D3D12_HEAP_TYPE_UPLOAD; + props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + + HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bd->pTexUploadBuffer)); + IM_ASSERT(SUCCEEDED(hr)); + + D3D12_RANGE range = {0, upload_size}; + hr = bd->pTexUploadBuffer->Map(0, &range, &bd->pTexUploadBufferMapped); + IM_ASSERT(SUCCEEDED(hr)); + bd->pTexUploadBufferSize = upload_size; + } bd->pTexCmdAllocator->Reset(); bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr); ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList; // Copy to upload buffer - void* mapped = nullptr; - D3D12_RANGE range = { 0, upload_size }; - hr = uploadBuffer->Map(0, &range, &mapped); - IM_ASSERT(SUCCEEDED(hr)); for (int y = 0; y < upload_h; y++) - memcpy((void*)((uintptr_t)mapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src); - uploadBuffer->Unmap(0, &range); + memcpy((void*)((uintptr_t)bd->pTexUploadBufferMapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src); if (need_barrier_before_copy) { @@ -567,7 +580,7 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; { - srcLocation.pResource = uploadBuffer; + srcLocation.pResource = bd->pTexUploadBuffer; srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; srcLocation.PlacedFootprint.Footprint.Width = upload_w; @@ -591,9 +604,8 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) cmdList->ResourceBarrier(1, &barrier); } - hr = cmdList->Close(); + HRESULT hr = cmdList->Close(); IM_ASSERT(SUCCEEDED(hr)); - ID3D12CommandQueue* cmdQueue = bd->pCommandQueue; cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue); @@ -606,7 +618,6 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent); ::WaitForSingleObject(bd->FenceEvent, INFINITE); - uploadBuffer->Release(); tex->SetStatus(ImTextureStatus_OK); } @@ -886,6 +897,13 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() bd->commandQueueOwned = false; SafeRelease(bd->pRootSignature); SafeRelease(bd->pPipelineState); + if (bd->pTexUploadBufferMapped) + { + D3D12_RANGE range = { 0, bd->pTexUploadBufferSize }; + bd->pTexUploadBuffer->Unmap(0, &range); + bd->pTexUploadBufferMapped = nullptr; + } + SafeRelease(bd->pTexUploadBuffer); SafeRelease(bd->pTexCmdList); SafeRelease(bd->pTexCmdAllocator); SafeRelease(bd->Fence); diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 668509052..16238f8c5 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -48,7 +48,7 @@ #include "imgui.h" -// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN +// When targeting native platforms (i.e. NOT Emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN // or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details. #ifndef __EMSCRIPTEN__ #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) diff --git a/backends/imgui_impl_wgpu.h b/backends/imgui_impl_wgpu.h index 5474f3c9c..99630101b 100644 --- a/backends/imgui_impl_wgpu.h +++ b/backends/imgui_impl_wgpu.h @@ -34,7 +34,7 @@ // Initialization data, for ImGui_ImplWGPU_Init() struct ImGui_ImplWGPU_InitInfo { - WGPUDevice Device; + WGPUDevice Device = nullptr; int NumFramesInFlight = 3; WGPUTextureFormat RenderTargetFormat = WGPUTextureFormat_Undefined; WGPUTextureFormat DepthStencilFormat = WGPUTextureFormat_Undefined; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 4a2a841e4..fe4b97636 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,78 +45,97 @@ Breaking Changes: io.ConfigViewportPlatformFocusSetsImGuiFocus to io.ConfigViewportsPlatformFocusSetsImGuiFocus. (#6299, #6462) It was really a typo in the first place, and introduced in 1.92.2. -- Backends: Vulkan: moved some fields in ImGui_ImplVulkan_InitInfo: - init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass - init_info.Subpass --> init_info.PipelineInfoMain.Subpass - init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples - init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo - It makes things more consistent and was desirable to introduce new settings for - secondary viewports. (#8946, #8110, #8111, #8686) [@ocornut, @SuperRonan, @sylmroz] -- Backends: Vulkan: renamed ImGui_ImplVulkan_MainPipelineCreateInfo --> ImGui_ImplVulkan_PipelineInfo - (introduced very recently and only used by `ImGui_ImplVulkan_CreateMainPipeline()` - so it should not affect many users). (#8110, #8111) -- Backends: Vulkan: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a - `VkImageUsageFlags image_usage` argument to specific extra flags for calls to - vkCreateSwapchainKHR() done for secondary viewports. We automatically add - `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT`. In theory the function is an internal - helper but since it's used by our examples some may have used it. (#8946, #8111, #8686) +- Backends: + - Vulkan: moved some fields in ImGui_ImplVulkan_InitInfo: + init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass + init_info.Subpass --> init_info.PipelineInfoMain.Subpass + init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples + init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo + It makes things more consistent and was desirable to introduce new settings for + secondary viewports. (#8946, #8110, #8111, #8686) [@ocornut, @SuperRonan, @sylmroz] + - Vulkan: renamed ImGui_ImplVulkan_MainPipelineCreateInfo --> ImGui_ImplVulkan_PipelineInfo + (introduced very recently and only used by `ImGui_ImplVulkan_CreateMainPipeline()` + so it should not affect many users). (#8110, #8111) + - Vulkan: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a + `VkImageUsageFlags image_usage` argument. + It was previously hardcoded to `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT` and defaults + to that when the value is 0. In theory the function is an internal helper but + since it's used by our examples some may have used it. (#8946, #8111, #8686) Other Changes: - Windows: added lower-right resize grip on child windows using both ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY flags. (#8501) [@aleksijuvani] The grip is not visible before hovering to reduce clutter. -- InputText: fixed single-line InputText() not applying fine character clipping - properly (regression in 1.92.3). (#8967) [@Cyphall] +- Style: added ImGuiCol_UnsavedMarker, color of the unsaved document marker when + using ImGuiWindowFlags_UnsavedDocument/ImGuiTabItemFlags_UnsavedDocument. (#8983) - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) +- Inputs: + - Shortcuts: added support for combining ImGuiInputFlags_RouteFocused + (which is the default route) with ImGuiInputFlags_RouteOverActive, allowing + to steal shortcuts from active item without using global routing. (#9004) +- InputText: + - Fixed single-line InputText() not applying fine character clipping + properly (regression in 1.92.3). (#8967) [@Cyphall] + - Fixed an infinite loop error happening if a custom input text + callback modifies/clear BufTextLen before calling InsertChars(). + (regression from 1.92.3). Note that this never really worked correctly, but + previously it would only temporary wreck cursor position, and since 1.92.3 it + would go in an infinite loop. (#8994, #3237) +- Textures: + - Fixed a crash if texture status is set to _WantDestroy by a backend after + it had already been destroyed. This would typically happen when calling backend's + ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811) + - Allowed backend to destroy texture while inside the NewFrame/EndFrame + scope. Basically if a backend decide to destroy a texture that we didn't request + to destroy (for e.g. freeing resources) the texture is immediately set to + a _WantCreate status again. (#8811) + - Fixed an issue preventing multi-contexts sharing a ImFontAtlas from + being possible to destroy in any order. + - Fixed not updating ImTextureData's RefCount when destroying a context + using a shared ImFontAtlas, leading standard backends to not properly + free texture resources. (#8975) [@icrashstuff] +- Demo: fixed layout issue in "Layout & Scrolling -> Scrolling" section. - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, Android Studio & more) to provide nicer display for ImVec2, ImVec4, ImVector etc. See misc/debuggers/ for details. (#8950) [@mentlerd] -- Textures: fixed a crash if texture status is set to _WantDestroy by a backend after - it had already been destroyed. This would typically happen when calling backend's - ImGui_ImplXXXX_InvalidateDeviceObjects() helpers twice in a row. (#8977, #8811) -- Textures: allowed backend to destroy texture while inside the NewFrame/EndFrame - scope. Basically if a backend decide to destroy a texture that we didn't request - to destroy (for e.g. freeing resources) the texture is immediately set to - a _WantCreate status again. (#8811) -- Textures: fixed an issue preventing multi-contexts sharing a ImFontAtlas from - being possible to destroy in any order. -- Textures: fixed not updating ImTextureData's RefCount when destroying a context - using a shared ImFontAtlas, leading standard backends to not properly free - texture resources. (#8975) [@icrashstuff] -- CI: Updates Windows CI scripts to generate/use VulkanSDK. (#8925, #8778) [@yaz0r] -- Backends: all backends call ImGuiPlatformIO::ClearPlatformHandlers() and - ClearRendererHandlers() on shutdown, so as not to leave function pointers - which may be dangling when using backend in e.g. DLL. (#8945, #2769) +- CI: updated Windows CI scripts to generate/use VulkanSDK. (#8925, #8778) [@yaz0r] - Docs: updated FAQ with new "What is the difference between Dear ImGui and traditional UI toolkits?" entry. (#8862) -- Backends: DirectX12: reuse a command list and allocator for texture uploads instead - of recreating them each time. (#8963, #8465) [@RT2Code] -- Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code] - (presumably fixes old hard-to-repro crash issues such as #3463, #5018) -- Backends: DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] -- Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support - `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] -- Backends: GLFW: fixed build on platform that are neither Windows, macOS or - known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion] -- Backends: SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our - window is hovered, as the event data is reliable and enough in this case. - - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786) - - Essentially a working for SDL3 bug which will be fixed in SDL 3.3.0. - - Better perf on X11 as querying global position requires a round trip to X11 server. -- Backends: Win32: minor optimization not submitting gamepad io again if - XInput's dwPacketNumber has not changed. (#8556) [@MidTerm-CN] -- Backends: Vulkan: added a way to specify custom shaders by filling init fields - CustomShaderVertCreateInfo and CustomShaderFragCreateInfo. (#8585, #8271) [@johan0A] -- Backends: DX9,DX10,DX11,DX12,Metal,Vulkan,WGPU,SDLRenderer2,SDLRenderer3: - ensure that a texture in _WantDestroy state always turn to _Destroyed even - if your underlying graphics data was already destroyed. (#8977) -- Examples: SDL2+DirectX11: Try WARP software driver if hardware driver is - not available. (#5924, #5562) -- Examples: SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] -- Examples: Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] -- Examples: made examples's main.cpp consistent with returning 1 on error. +- Backends: + - All backends call ImGuiPlatformIO::ClearPlatformHandlers() and + ClearRendererHandlers() on shutdown, so as not to leave function pointers + which may be dangling when using backend in e.g. DLL. (#8945, #2769) + - DirectX12: reuse a command list and allocator for texture uploads instead + of recreating them each time. (#8963, #8465) [@RT2Code] + - DirectX12: Rework synchronization logic. (#8961) [@RT2Code] + (presumably fixes old hard-to-repro crash issues such as #3463, #5018) + - DirectX12: Reuse texture upload buffer and grow it only when + necessary. (#9002) [@RT2Code] + - DirectX12: Enable swapchain tearing if available. (#8965) [@RT2Code] + - OpenGL3: fixed GL loader to work on Haiku OS which does not support + `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] + - GLFW: fixed build on platform that are neither Windows, macOS or + known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion] + - SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our + window is hovered, as the event data is reliable and enough in this case. + - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786) + - Essentially a workaround for SDL3 bug which will be fixed in SDL 3.3.0. + - Better perf on X11 as querying global position requires a round trip to X11 server. + - Win32: minor optimization not submitting gamepad io again if + XInput's dwPacketNumber has not changed. (#8556) [@MidTerm-CN] + - Vulkan: added a way to specify custom shaders by filling init fields + CustomShaderVertCreateInfo and CustomShaderFragCreateInfo. (#8585, #8271) [@johan0A] + - DX9,DX10,DX11,DX12,Metal,Vulkan,WGPU,SDLRenderer2,SDLRenderer3: + ensure that a texture in _WantDestroy state always turn to _Destroyed even + if your underlying graphics data was already destroyed. (#8977) +- Examples: + - SDL2+DirectX11: Try WARP software driver if hardware driver is + not available. (#5924, #5562) + - SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] + - Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] + - Made examples's main.cpp consistent with returning 1 on error. Docking+Viewports Branch: diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index 5eec5009d..a4c884a54 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for using GLFW + WebGPU +// Dear ImGui: standalone example application for GLFW + WebGPU // - Emscripten is supported for publishing on web. See https://emscripten.org. // - Dawn is used as a WebGPU implementation on desktop. @@ -12,36 +12,32 @@ #include "imgui_impl_glfw.h" #include "imgui_impl_wgpu.h" #include +#include +// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. #ifdef __EMSCRIPTEN__ #include #include #include +#include "../libs/emscripten/emscripten_mainloop_stub.h" #else #include #endif - -#include #include #include -// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. -#ifdef __EMSCRIPTEN__ -#include "../libs/emscripten/emscripten_mainloop_stub.h" -#endif - -// Global WebGPU required states +// Data static WGPUInstance wgpu_instance = nullptr; static WGPUDevice wgpu_device = nullptr; static WGPUSurface wgpu_surface = nullptr; static WGPUTextureFormat wgpu_preferred_fmt = WGPUTextureFormat_RGBA8Unorm; static WGPUSwapChain wgpu_swap_chain = nullptr; -static int wgpu_swap_chain_width = 1280; -static int wgpu_swap_chain_height = 800; +static int wgpu_surface_width = 1280; +static int wgpu_surface_height = 800; // Forward declarations static bool InitWGPU(GLFWwindow* window); -static void CreateSwapChain(int width, int height); +static void ResizeSurface(int width, int height); static void glfw_error_callback(int error, const char* description) { @@ -72,19 +68,23 @@ int main(int, char**) // Make sure GLFW does not initialize any graphics context. // This needs to be done explicitly later. glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - GLFWwindow* window = glfwCreateWindow(wgpu_swap_chain_width, wgpu_swap_chain_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); + + // Create window + float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only + wgpu_surface_width *= main_scale; + wgpu_surface_height *= main_scale; + GLFWwindow* window = glfwCreateWindow(wgpu_surface_width, wgpu_surface_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); if (window == nullptr) return 1; // Initialize the WebGPU environment if (!InitWGPU(window)) { - if (window) - glfwDestroyWindow(window); + glfwDestroyWindow(window); glfwTerminate(); return 1; } - CreateSwapChain(wgpu_swap_chain_width, wgpu_swap_chain_height); + ResizeSurface(wgpu_surface_width, wgpu_surface_height); glfwShowWindow(window); // Setup Dear ImGui context @@ -99,6 +99,11 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose) + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOther(window, true); #ifdef __EMSCRIPTEN__ @@ -118,15 +123,14 @@ int main(int, char**) // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. // - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author! // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - // - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details. - //io.Fonts->AddFontDefault(); + // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details. //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefault(); #ifndef IMGUI_DISABLE_FILE_FUNCTIONS //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf"); - //io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf"); //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf"); //IM_ASSERT(font != nullptr); #endif @@ -161,10 +165,10 @@ int main(int, char**) // React to changes in screen size int width, height; glfwGetFramebufferSize((GLFWwindow*)window, &width, &height); - if (width != wgpu_swap_chain_width || height != wgpu_swap_chain_height) + if (width != wgpu_surface_width || height != wgpu_surface_height) { ImGui_ImplWGPU_InvalidateDeviceObjects(); - CreateSwapChain(width, height); + ResizeSurface(width, height); ImGui_ImplWGPU_CreateDeviceObjects(); } @@ -239,8 +243,8 @@ int main(int, char**) WGPUCommandBufferDescriptor cmd_buffer_desc = {}; WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); - WGPUQueue queue = wgpuDeviceGetQueue(wgpu_device); - wgpuQueueSubmit(queue, 1, &cmd_buffer); + WGPUQueue wgpu_queue = wgpuDeviceGetQueue(wgpu_device); + wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer); #ifndef __EMSCRIPTEN__ wgpuSwapChainPresent(wgpu_swap_chain); @@ -312,10 +316,10 @@ static bool InitWGPU(GLFWwindow* window) #endif #ifdef __EMSCRIPTEN__ - wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {}; - html_surface_desc.selector = "#canvas"; + wgpu::SurfaceDescriptorFromCanvasHTMLSelector canvas_desc = {}; + canvas_desc.selector = "#canvas"; wgpu::SurfaceDescriptor surface_desc = {}; - surface_desc.nextInChain = &html_surface_desc; + surface_desc.nextInChain = &canvas_desc; wgpu::Surface surface = instance.CreateSurface(&surface_desc); wgpu::Adapter adapter = {}; @@ -327,6 +331,7 @@ static bool InitWGPU(GLFWwindow* window) wgpu_preferred_fmt = WGPUTextureFormat_BGRA8Unorm; #endif + // Moving Dawn objects into WGPU handles wgpu_instance = instance.MoveToCHandle(); wgpu_surface = surface.MoveToCHandle(); @@ -335,12 +340,12 @@ static bool InitWGPU(GLFWwindow* window) return true; } -static void CreateSwapChain(int width, int height) +static void ResizeSurface(int width, int height) { if (wgpu_swap_chain) wgpuSwapChainRelease(wgpu_swap_chain); - wgpu_swap_chain_width = width; - wgpu_swap_chain_height = height; + wgpu_surface_width = width; + wgpu_surface_height = height; WGPUSwapChainDescriptor swap_chain_desc = {}; swap_chain_desc.usage = WGPUTextureUsage_RenderAttachment; swap_chain_desc.format = wgpu_preferred_fmt; diff --git a/imconfig.h b/imconfig.h index 4dab1b604..0e7cf6182 100644 --- a/imconfig.h +++ b/imconfig.h @@ -83,6 +83,7 @@ //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). +// Note that imgui_freetype.cpp may be used _without_ this define, if you manually call ImFontAtlas::SetFontLoader(). The define is simply a convenience. // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. //#define IMGUI_ENABLE_FREETYPE diff --git a/imgui.cpp b/imgui.cpp index 4a1e25cfe..f7c9fd2f1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3767,6 +3767,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; case ImGuiCol_TreeLines: return "TreeLines"; case ImGuiCol_DragDropTarget: return "DragDropTarget"; + case ImGuiCol_UnsavedMarker: return "UnsavedMarker"; case ImGuiCol_NavCursor: return "NavCursor"; case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg"; @@ -7551,7 +7552,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; if (marker_pos.x > layout_r.Min.x) { - RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker)); clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); } } @@ -7572,7 +7573,7 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags } if (parent_window && (flags & ImGuiWindowFlags_Popup)) window->RootWindowPopupTree = parent_window->RootWindowPopupTree; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ? + if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip))) // FIXME: simply use _NoTitleBar ? window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened) { @@ -7717,7 +7718,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; + ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); // We allow window memory to be compacted so recreate the base stack when needed. @@ -9997,7 +9998,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner; - routing_entry->RoutingNextScore = 255; + routing_entry->RoutingNextScore = 0; if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner) continue; rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer @@ -10066,23 +10067,24 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) return routing_data; } -// Current score encoding (lower is highest priority): -// - 0: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive -// - 1: ImGuiInputFlags_ActiveItem or ImGuiInputFlags_RouteFocused (if item active) -// - 2: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused -// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack) -// - 254: ImGuiInputFlags_RouteGlobal -// - 255: never route +// Current score encoding +// - 0: Never route +// - 1: ImGuiInputFlags_RouteGlobal (lower priority) +// - 100..199: ImGuiInputFlags_RouteFocused (if window in focus-stack) +// 200: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused +// 300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active) +// 400: ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverActive +// - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority) // 'flags' should include an explicit routing policy static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; if (flags & ImGuiInputFlags_RouteFocused) { - // ActiveID gets top priority + // ActiveID gets high priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) if (owner_id != 0 && g.ActiveId == owner_id) - return 1; + return 300; // Score based on distance to focused window (lower is better) // Assuming both windows are submitting a routing request, @@ -10092,25 +10094,32 @@ static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInput // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. // This essentially follow the window->ParentWindowForFocusRoute chain. if (focus_scope_id == 0) - return 255; + return 0; for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) - return 3 + index_in_focus_path; - return 255; + { + if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id) + return 599 - index_in_focus_path; + else + return 199 - index_in_focus_path; + } + return 0; } else if (flags & ImGuiInputFlags_RouteActive) { if (owner_id != 0 && g.ActiveId == owner_id) - return 1; - return 255; + return 300; + return 0; } else if (flags & ImGuiInputFlags_RouteGlobal) { if (flags & ImGuiInputFlags_RouteOverActive) - return 0; + return 400; + if (owner_id != 0 && g.ActiveId == owner_id) + return 300; if (flags & ImGuiInputFlags_RouteOverFocused) - return 2; - return 254; + return 200; + return 1; } IM_ASSERT(0); return 0; @@ -10150,8 +10159,10 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I else IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner); - if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused)) + if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused)) IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal); + if (flags & ImGuiInputFlags_RouteOverActive) + IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused)); // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. key_chord = FixupKeyChord(key_chord); @@ -10206,17 +10217,17 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, I const int score = CalcRoutingScore(focus_scope_id, owner_id, flags); IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score); - if (score == 255) + if (score == 0) return false; // Submit routing for NEXT frame (assuming score is sufficient) - // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <). + // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >). ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); - //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore); - if (score < routing_data->RoutingNextScore) + //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore); + if (score > routing_data->RoutingNextScore) { routing_data->RoutingNext = owner_id; - routing_data->RoutingNextScore = (ImU8)score; + routing_data->RoutingNextScore = (ImU16)score; } // Return routing state for CURRENT frame diff --git a/imgui.h b/imgui.h index 251b016bf..27baa6baf 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.4 WIP" -#define IMGUI_VERSION_NUM 19234 +#define IMGUI_VERSION_NUM 19236 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 #define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch. @@ -1851,6 +1851,7 @@ enum ImGuiCol_ ImGuiCol_TextSelectedBg, // Selected text inside an InputText ImGuiCol_TreeLines, // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines ImGuiCol_DragDropTarget, // Rectangle highlighting a drop target + ImGuiCol_UnsavedMarker, // Unsaved Document marker (in window title and tabs) ImGuiCol_NavCursor, // Color of keyboard/gamepad navigation cursor/rectangle, when visible ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB ImGuiCol_NavWindowingDimBg, // Darken/colorize entire screen behind the CTRL+TAB window list, when active diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ff5281dfb..4c5066b5d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -4879,15 +4879,18 @@ static void DemoWindowLayout() ImGui::Checkbox("Decoration", &enable_extra_decorations); + ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); + ImGui::SameLine(); ImGui::Checkbox("Track", &enable_track); - ImGui::PushItemWidth(100); - ImGui::SameLine(140); enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d"); - bool scroll_to_off = ImGui::Button("Scroll Offset"); - ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); + bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px"); + ImGui::SameLine(); + scroll_to_off |= ImGui::Button("Scroll Offset"); - bool scroll_to_pos = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); + bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px"); + ImGui::SameLine(); + scroll_to_pos |= ImGui::Button("Scroll To Pos"); ImGui::PopItemWidth(); if (scroll_to_off || scroll_to_pos) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index afc1e0900..f5c773c0b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -242,6 +242,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_NavCursor] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); @@ -309,6 +310,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); @@ -377,6 +379,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); colors[ImGuiCol_TreeLines] = colors[ImGuiCol_Border]; colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); + colors[ImGuiCol_UnsavedMarker] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); colors[ImGuiCol_NavCursor] = colors[ImGuiCol_HeaderHovered]; colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.20f); diff --git a/imgui_internal.h b/imgui_internal.h index b7eb659d5..72461e4ef 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1578,12 +1578,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. - ImU8 RoutingCurrScore; // [DEBUG] For debug display - ImU8 RoutingNextScore; // Lower is better (0: perfect score) + ImU16 RoutingCurrScore; // [DEBUG] For debug display + ImU16 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; - ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. @@ -2511,7 +2511,7 @@ struct ImGuiContext float NavHighlightActivatedTimer; ImGuiID NavNextActivateId; // Set by ActivateItemByID(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data. ImS8 NavCursorHideFrames; //ImGuiID NavActivateInputId; // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606. @@ -2691,7 +2691,7 @@ struct ImGuiContext ImGuiWindow* LogWindow; ImFileHandle LogFile; // If != NULL log to stdout/ file ImGuiTextBuffer LogBuffer; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - const char* LogNextPrefix; + const char* LogNextPrefix; // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully! const char* LogNextSuffix; float LogLinePosY; bool LogLineFirstItem; @@ -4219,7 +4219,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) #define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) +#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) #endif diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index c5cafbeb4..354d5d54a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3947,6 +3947,7 @@ static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, c { ImGuiContext& g = *ctx; ImGuiInputTextState* obj = &g.InputTextState; + IM_ASSERT(text_end_display >= text_begin && text_end_display <= text_end); return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags); } @@ -4327,11 +4328,12 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); Buf[BufTextLen + new_text_len] = '\0'; - if (CursorPos >= pos) - CursorPos += new_text_len; - SelectionStart = SelectionEnd = CursorPos; BufDirty = true; BufTextLen += new_text_len; + if (CursorPos >= pos) + CursorPos += new_text_len; + CursorPos = ImClamp(CursorPos, 0, BufTextLen); + SelectionStart = SelectionEnd = CursorPos; } void ImGui::PushPasswordFont() @@ -10840,8 +10842,8 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered); if (unsaved_marker_visible) { - const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz)); - RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); + ImVec2 bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5f; + RenderBullet(draw_list, bullet_pos, GetColorU32(ImGuiCol_UnsavedMarker)); } else if (close_button_visible) { diff --git a/misc/freetype/README.md b/misc/freetype/README.md index e1bd0198b..6ea6ad1ae 100644 --- a/misc/freetype/README.md +++ b/misc/freetype/README.md @@ -7,7 +7,7 @@ Build font atlases using FreeType instead of stb_truetype (which is the default 1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype --triplet=x64-windows`, `vcpkg integrate install`). 2. Add imgui_freetype.h/cpp alongside your project files. -3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file +3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file to make Dear ImGui automatically use the imgui_freetype loader. If your copy Dear ImGui is precompiled, you can always enable imgui_freetype by calling ImFontAtlas::SetFontLoader(). ### About Gamma Correct Blending