diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 06dfd0527..020e8a07e 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -330,11 +330,31 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModuleWGSL(con WGPUShaderModuleDescriptor desc = {}; desc.nextInChain = (WGPUChainedStruct*)&wgsl_desc; + // Detect shader compilation errors by using an error scope. + // Flag to be passed into the validation callback `userdata1` pointer. + int validation_error = 0; + wgpuDevicePushErrorScope(bd->wgpuDevice, WGPUErrorFilter_Validation); + WGPUShaderModule module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); + WGPUPopErrorScopeCallbackInfo pop_cb = {}; + pop_cb.mode = WGPUCallbackMode_AllowSpontaneous; + pop_cb.callback = [](WGPUPopErrorScopeStatus, WGPUErrorType type, WGPUStringView, void* userdata1, void*) + { + if (type == WGPUErrorType_Validation) + *static_cast(userdata1) = 1; + }; + pop_cb.userdata1 = &validation_error; + wgpuDevicePopErrorScope(bd->wgpuDevice, pop_cb); + WGPUProgrammableStageDescriptor stage_desc = {}; -#if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK) - stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); - stage_desc.entryPoint = { "main", WGPU_STRLEN }; -#endif + if (module && !validation_error) + { + stage_desc.module = module; + stage_desc.entryPoint = { "main", WGPU_STRLEN }; + } + else if (module) + { + wgpuShaderModuleRelease(module); + } return stage_desc; } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0ac2f967a..edf30407a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,17 +36,14 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.92.8 WIP (In Progress) + VERSION 1.92.8 (Released 2026-05-12) ----------------------------------------------------------------------- +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.8 + Breaking Changes: -- DrawList: - - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, - which is part of our new standard draw callbacks. (#9378) - Redirecting the earlier value into the later one when set, so both old and new code should work. -- DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). - Recap: +- DrawList: swapped the last two arguments of `AddRect()`, `AddPolyline()`, `PathStroke()`. - Before: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);` - After: `void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0);` - Before: `void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);` @@ -55,36 +52,40 @@ Breaking Changes: - After: `void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0);` Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. - This is not an easy change but it makes ImDrawList function signatures consistent. - As we are aiming to add flags and features to variety of ImDrawList functions, that consistency will become particularly important. - The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. Effectively the typical call site is changing from: - Before: `window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size);` - After: `window->DrawList->AddRect(p_min, p_max, color, rounding, border_size);` Notes: - - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes. - Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value. + If you are a binding maintainer consider doing something to facilitate transition or error detection. + - This is perhaps the worst breaking change in our history :( but it makes ImDrawList function signatures consistent. + As we are aiming to add flags and features to variety of ImDrawList functions, that consistency becomes more important. + The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. + - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. +- DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, + which is part of our new standard draw callbacks. (#9378) + Redirecting the earlier value into the later one when set, so both old and new code should work. - Backends: - Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. (#914) This change allows us to facilitate changing samplers, in line with other backends. [@yaz0r, @ocornut] - When creating your own descriptor pool (instead of letting backend creates its own): - - Before: need at least IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER. - - After: need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. - + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. + - Before: need at least `IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER`. + - After: need at least `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`. + + `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLER`. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. - - Before: ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout) - - After: ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout) + - Before: `ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout)` + - After: `ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout)` - Kept inline redirection function that ignores the sampler (will obsolete). - - DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from ImGui_ImplXXXX_RenderState. + - DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from `ImGui_ImplXXXX_RenderState`. Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378) If there is a legit need/request for them or any render state we can always add them back. Other Changes: - DrawList: - - Added room in ImGuiPlatformIO for standard backend-agnostic draw callbacks. Those callbacks + - Added room in `ImGuiPlatformIO` for standard backend-agnostic draw callbacks. Those callbacks are setup/provided by the backend and available in most of our standard backends. They allow backend-agnostic code from e.g. switching to a Nearest/Point sampler without messing with custom Renderer-specific callbacks. @@ -94,13 +95,13 @@ Other Changes: Note that some backends might not support all callbacks. (#9378, #9371, #3590, #8926, #2973, #7485, #7468, #6969, #5118, #7616, #9173, #8322, #7230, #5999, #6452, #5156, #7342, #7592, #7511) - - Made AddCallback() user data default to Null for convenience. - - Added AddLineH(), AddLineV() helpers to draw horizontal and vertical lines. [@memononen] + - Made `AddCallback()` user data default to Null for convenience. + - Added `AddLineH()`, `AddLineV()` helpers to draw horizontal and vertical lines. [@memononen] - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. - Fixed a crash when toggling ReadOnly while active. (#9354) - - CharFilter callback event sets CursorPos/SelectionStart/SelectionEnd. (#816) + - `CharFilter` callback event sets CursorPos/SelectionStart/SelectionEnd. (#816) - Tables: - Fixed issues reporting ideal size to parent window/container: (#9352, #7651) - When both scrollbars are visible but only one of ScrollX/ScrollY was explicitly requested. @@ -109,51 +110,72 @@ Other Changes: - Fixed a single-axis auto-resizing feedback loop issue with nested containers and varying scrollbar visibility. (#9352) - Detect and report error when calling End() instead of EndPopup() on a popup. (#9351) - - Child windows with only ImGuiChildFlags_AutoResizeY flag keep using the proportional - default ItemWidth. (#9355) -- InputInt, InputFloat, InputScalar: reinstated ImGuiInputTextFlags_EnterReturnsTrue - support which was removed in 1.91.4. (#8665, #9299, #8065, #3946, #6284, #9117) - - Fixed the fact that it didn't return true when validating same value. - - Fixed losing value when tabbing out or losing focus. - - Made it that pressing +/- step buttons also return true, which is in line - with 1.91.4 behavior. - - In a majority of cases you should use IsItemDeactivatedAfterEdit() instead, - but it still has a few edge cases flaws (to be addressed soon). -- InputInt, InputFloat, InputScalar: allow passing a format string that does not display - the scalar value. Parsing input with default format for the type. (#9385) [@FireFox2000000] + - Child windows with only `ImGuiChildFlags_AutoResizeY` flag keep using the proportional + default `ItemWidth`. (#9355) + - Using mouse wheel to scroll takes and keeps ownership of the corresponding keys + (e.g. `ImGuiKey_MouseWheelY`) while a wheeling window is locked. (#2604, #3795) +- InputInt, InputFloat, InputScalar: + - Reinstated `ImGuiInputTextFlags_EnterReturnsTrue` support which was removed in 1.91.4. + (#8665, #9299, #8065, #3946, #6284, #9117) + - Fixed the fact that it didn't return true when validating same value. + - Fixed losing value when tabbing out or losing focus. + - Made it that pressing +/- step buttons also return true, which is in line + with 1.91.4 behavior. + - In a majority of cases you should use `IsItemDeactivatedAfterEdit()` instead, + but it still has a few edge cases flaws (to be addressed soon). + - Allow passing a format string that does not display the scalar value. + Parsing input with default format for the type. (#9385) [@FireFox2000000] - Multi-Select: - Fixed an issue using Multi-Select within a Table causing column width measurement to be invalid when trailing column contents is not submitted in the last row. (#9341, #8250) - Fixed an issue using Multi-Select within a Table with the right-most column visible, which could lead to an extra vertical offset in the Header row. (#8250) - - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect1d mode while scrolling. +- Multi-Select + Box-Select: + - Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect1d` mode while scrolling. Notably, using mouse wheel while holding a box-selection could lead items close to windows edges from not being correctly unselected. (#7994, #8250, #7821, #7850, #7970) - - Box-Select: improved dirty/unclip rectangle logic for ImGuiMultiSelectFlags_BoxSelect2d. - - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect2d mode, where + - Improved dirty/unclip rectangle logic for `ImGuiMultiSelectFlags_BoxSelect2d`. + - Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect2d` mode, where items out of view wouldn't be properly selected while scrolling while mouse cursor is hovering outside of selection scope. (#7994, #1861, #6518) - - Box-Select: fixed an issue where items out of horizontal view would sometimes lead + - Fixed an issue where items out of horizontal view would sometimes lead to incorrect merging of sequential selection requests while also scrolling fast enough to overlap multiple rows during a frame. (#7994, #1861, #6518) - - Box-Select: fixed an issue using ImGuiMultiSelectFlags_BoxSelect2d mode in a Table - while relying on the TableNextColumn() return value to perform coarse clipping. (#7994) - - Box-Select: disabled merging consecutive selection requests as we have no reliable - way of detecting if user has submitted all consecutives items without clipping gaps, - and ImGuiSelectionUserData is technically opaque storage. (#7994, #1861) + - Fixed an issue using `ImGuiMultiSelectFlags_BoxSelect2d` mode in a Table + while relying on the `TableNextColumn()` return value to perform coarse clipping. (#7994) + - Disabled merging consecutive selection requests as we have no reliable + way of detecting if user has submitted all consecutive items without clipping gaps, + and `ImGuiSelectionUserData` is technically opaque storage. (#7994, #1861) (we will probably bring this back as a minor optimization if we have a way to for - user to tell us ImGuiSelectionUserData are indices) - - Box-Select: fixes for using accross nested child windows. (#8364) + user to tell us `ImGuiSelectionUserData` are indices) + - Fixes for using across nested child windows. (#8364) - Box-Select + Clipper: fixed an issue selecting items while scrolling while a clipper active. (#7994, #8250, #7821, #7850, #7970) - Box-Select + Tables: fixed an issue using box-selection in a tables with items straying out of columns boundaries. (#7994, #2221) - Box-Select + Tables: fixed an issue when calling `BeginMultiSelect()` in a table before layout has been locked (first row or headers row submitted). (#8250) +- Menus: + - BeginMenu()/MenuItem(): fixed accidental triggering of child menu items when + opening a menu inside a small host window forcing the child menu window to be + repositioned under the mouse cursor. (#8233, #9394) + Done by reworking `BeginMenu()`/`MenuItem()`: they previously avoiding taking + ActiveID + key/click ownership (in order to allow releasing button on another item). + Now they take them and release them once the mouse is moved outside item boundaries. +- Inputs: + - SetItemKeyOwner(): return true if ownership has been requested, which typically + needs to to checked for gating further tests. This is important as the function + may fail. (#456, #2637, #2620, #2891, #3370, #3724, #4828, #5108, #5242, #5641) + - SetItemKeyOwner(): does not set ownership is key is already taken. Effectively + this makes using `SetItemKeyOwner(ImGuiKey_MouseWheelY)` over an item work as + expected while not having item react if a scroll wheel is actively in progress. + May be subject to further redesign, e.g. conditional flags. Feedback welcome! - Style: + - Checkbox: added `ImGuiCol_CheckboxSelectedBg` to change or accentuate the + background color of checked/mixed checkboxes. (#9392) + - Added `ImGuiStyleVar_DragDropTargetRounding`. (#9056) - Fixed vertical scrollbar top coordinates when using thick borders on windows with no title bar and no menu bar. (#9366) - - Added ImGuiStyleVar_DragDropTargetRounding. (#9056) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Assert when using MergeMode with an explicit size over a target font with an implicit @@ -164,8 +186,12 @@ Other Changes: - Tweaked assert triggering when first item height measurement fails, and made it a better recoverable error. (#9350) - Misc: - - Minor optimization: reduce redudant label scanning in common widgets. - - Added missing Test Engine hooks for PlotXXX(), VSliderXXX(), TableHeader(). + - Minor optimization: reduce redundant label scanning in common widgets. + - Added missing Test Engine hooks for `PlotLines()`, `PlotHistogram()`, `VSliderXXX()` + and `TableHeader()` functions. +- Demo: + - Added simple demo for a scrollable/zoomable image viewer with a grid. + Available in `Examples->Image Viewer` and `Widgets->Images`. - Backends: - Added support for new standardized draw callbacks in most backends: (#9378) - Allegro5: Reset n/a n/a @@ -191,20 +217,20 @@ Other Changes: - SDL2: made `ImGui_ImplSDL2_GetContentScaleForWindow()`/`ImGui_ImplSDL2_GetContentScaleForDisplay()` helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369) - - WebGPU: always use SPIR-V shader on WGVK, as it cannot be detected at runtime. (#9316, #9246, #9257) + - WebGPU: rework choice/detection of using WGSL/SPIR-V shader on WGVK. (#9316, #9246, #9257, #9387) - Examples: - Update VS toolset in all .vcxproj from VS2015 (v140) to VS2017 (v141). The later is the first that supports vcpkg. Onward we will likely stop testing building the library with VS2015. - GLFW+Vulkan, SDL2+Vulkan, SDL3+Vulkan, Win32+Vulkan: reworked to create a descriptor pool with: - - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE. - - IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. + - `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`. + - `IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE` descriptors of type `VK_DESCRIPTOR_TYPE_SAMPLER`. Docking+Viewports Branch: - Docking: - Toggling tab bar visibility marks saved settings as dirty. (#9380) - Viewports: - - Added opaque `void* PlatformIconData` storage in viewport and ImGuiWindowClass + - Added opaque `void* PlatformIconData` storage in viewport and `ImGuiWindowClass` to allow passing icon information to a custom backend or hook. (#2715) diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 000000000..f2549efd0 --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,17 @@ +# Security Policy + +Dear ImGui is primarily designed for developer tools and technical applications running trusted code and trusted data. + +Given that the library was not designed as a hardened security boundary against hostile or intentionally malformed inputs: +consider not using it in a context where a crash (through e.g. intentionally malformed inputs) could lead to privilege elevation. +E.g. running in a privileged process and interacting with non-privileged clients feeding code or data to Dear ImGui. + +Dear ImGui development efforts are focused on improving developer experience, usability, correctness, and robustness in real-world applications. +We will do our best to reduce e.g. crashes caused by common programmer mistakes. + +However, the project does not specifically focus on adversarial fuzzing scenarios, allocation-failure hardening, or highly artificial edge cases that are unlikely to occur in normal usage. + +## Reporting a Vulnerability + +Considering the above policy, most things may be reported in GitHub Issues. +If you have a reason to disclose privately, please reach out to the contact address listed in README. diff --git a/examples/example_glfw_wgpu/CMakeLists.txt b/examples/example_glfw_wgpu/CMakeLists.txt index 53bcda339..95d9fe38a 100644 --- a/examples/example_glfw_wgpu/CMakeLists.txt +++ b/examples/example_glfw_wgpu/CMakeLists.txt @@ -146,6 +146,28 @@ else() # Native/Desktop build endif() if(IMGUI_WGVK_DIR) + find_package(Vulkan REQUIRED) + set(WGVK_PLATFORM_LIBS) + set(WGVK_PLATFORM_DEFS) + if(WIN32) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WIN32_SURFACE=1) + elseif(APPLE) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_METAL_SURFACE=1) + elseif(UNIX) + find_package(PkgConfig QUIET) + pkg_check_modules(WAYLAND_CLIENT QUIET wayland-client) + if(WAYLAND_CLIENT_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WAYLAND_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS ${WAYLAND_CLIENT_LIBRARIES}) + endif() + find_package(X11 QUIET) + if(X11_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_XLIB_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS X11::X11) + endif() + list(APPEND WGVK_PLATFORM_LIBS m dl pthread) + endif() + set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() @@ -187,6 +209,7 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(IMGUI_WGVK_DIR) target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_compile_definitions(${IMGUI_EXECUTABLE} PRIVATE ${WGVK_PLATFORM_DEFS}) target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) if (MSVC) target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) diff --git a/examples/example_sdl2_wgpu/CMakeLists.txt b/examples/example_sdl2_wgpu/CMakeLists.txt index 3bed79fd4..aa8a1e575 100644 --- a/examples/example_sdl2_wgpu/CMakeLists.txt +++ b/examples/example_sdl2_wgpu/CMakeLists.txt @@ -141,6 +141,28 @@ else() # Native/Desktop build endif() if(IMGUI_WGVK_DIR) + find_package(Vulkan REQUIRED) + set(WGVK_PLATFORM_LIBS) + set(WGVK_PLATFORM_DEFS) + if(WIN32) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WIN32_SURFACE=1) + elseif(APPLE) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_METAL_SURFACE=1) + elseif(UNIX) + find_package(PkgConfig QUIET) + pkg_check_modules(WAYLAND_CLIENT QUIET wayland-client) + if(WAYLAND_CLIENT_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WAYLAND_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS ${WAYLAND_CLIENT_LIBRARIES}) + endif() + find_package(X11 QUIET) + if(X11_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_XLIB_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS X11::X11) + endif() + list(APPEND WGVK_PLATFORM_LIBS m dl pthread) + endif() + set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() @@ -183,6 +205,7 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(IMGUI_WGVK_DIR) target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_compile_definitions(${IMGUI_EXECUTABLE} PRIVATE ${WGVK_PLATFORM_DEFS}) target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) if (MSVC) target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) diff --git a/examples/example_sdl3_wgpu/CMakeLists.txt b/examples/example_sdl3_wgpu/CMakeLists.txt index 85de861e3..7ecf0266d 100644 --- a/examples/example_sdl3_wgpu/CMakeLists.txt +++ b/examples/example_sdl3_wgpu/CMakeLists.txt @@ -141,6 +141,28 @@ else() # Native/Desktop build endif() if(IMGUI_WGVK_DIR) + find_package(Vulkan REQUIRED) + set(WGVK_PLATFORM_LIBS) + set(WGVK_PLATFORM_DEFS) + if(WIN32) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WIN32_SURFACE=1) + elseif(APPLE) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_METAL_SURFACE=1) + elseif(UNIX) + find_package(PkgConfig QUIET) + pkg_check_modules(WAYLAND_CLIENT QUIET wayland-client) + if(WAYLAND_CLIENT_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_WAYLAND_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS ${WAYLAND_CLIENT_LIBRARIES}) + endif() + find_package(X11 QUIET) + if(X11_FOUND) + list(APPEND WGVK_PLATFORM_DEFS SUPPORT_XLIB_SURFACE=1) + list(APPEND WGVK_PLATFORM_LIBS X11::X11) + endif() + list(APPEND WGVK_PLATFORM_LIBS m dl pthread) + endif() + set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() @@ -182,6 +204,7 @@ if(NOT EMSCRIPTEN) # WegGPU-Native settings if(IMGUI_WGVK_DIR) target_sources(${IMGUI_EXECUTABLE} PRIVATE ${IMGUI_WGVK_DIR}/src/wgvk.c) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGVK") + target_compile_definitions(${IMGUI_EXECUTABLE} PRIVATE ${WGVK_PLATFORM_DEFS}) target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGVK_DIR}/include) if (MSVC) target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /std:clatest /experimental:c11atomics) diff --git a/imgui.cpp b/imgui.cpp index b6b2e4889..09971db0e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (main code and documentation) // Help: @@ -403,7 +403,6 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - likewise io.MousePos and GetMousePos() will use OS coordinates. If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - 2026/05/07 (1.92.8) - DrawList: swapped the last two arguments of AddRect(), AddPolyline(), PathStroke(). - Recap: - Before: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); - After: void ImDrawList::AddRect(ImVec2 p_min, ImVec2 p_max, ImU32 col, float rounding = 0.0f, float thickness = 1.0f, ImDrawFlags flags = 0); - Before: void ImDrawList::AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); @@ -412,18 +411,19 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - After: void ImDrawList::PathStroke(ImU32 col, float thickness = 1.0f, ImDrawFlags flags = 0); Added inline redirection functions when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is off. Marked the old functions are =delete when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is on, to allow for better type-checking. - This is not an easy change but it makes ImDrawList function signatures consistent. - As we are aiming to add flags and features to variety of ImDrawList functions, that consistency will become particularly important. - The new order is also more convenient as 'flags' are less frequently used than 'thickness' in real code. Effectively the typical call site is changing from: - - Before: window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size); - - After: window->DrawList->AddRect(p_min, p_max, color, rounding, border_size); + - Before: window->DrawList->AddRect(p_min, p_max, color, rounding, ImDrawFlags_None, border_size); + - After: window->DrawList->AddRect(p_min, p_max, color, rounding, border_size); Notes: - - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - - Users of C++ and other languages with type-checking should be notified at compile-time of any mistakes. + - Users of C++ and other languages with type-checking will be notified at compile-time of any mistakes. - Users of high-level bindings or languages with no type-checking will be notified at runtime via an assert for invalid flags value. + If you are a binding maintainer consider doing something to facilitate transition or error detection. + - This is perhaps the worst breaking change in our history :( but it makes ImDrawList function signatures consistent. + As we are aiming to add flags and features to variety of ImDrawList functions, that consistency becomes more important. + The new order is also more convenient as `flags` are less frequently used than `thickness` in real code. + - As a general policy in Dear ImGui, all our flags default to 0 so ImDrawFlags_None was likely written 0 in some call sites. - Consider adding `#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS` in your imconfig.h, even temporarily, to clean up legacy code. - - 2026/04/23 (1.92.8) - Obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) + - 2026/04/23 (1.92.8) - DrawList: obsoleted `ImDrawCallback_ResetRenderState` in favor of using `ImGui::GetPlatformIO().DrawCallback_ResetRenderState`, which is part of our new standard draw callbacks. (#9378) - 2026/04/22 (1.92.8) - Backends: Vulkan: redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler. - When creating your own descriptor pool (instead of letting backend creates its own): need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER. @@ -3853,6 +3853,7 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; case ImGuiCol_CheckMark: return "CheckMark"; + case ImGuiCol_CheckboxSelectedBg: return "CheckboxSelectedBg"; case ImGuiCol_SliderGrab: return "SliderGrab"; case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; case ImGuiCol_Button: return "Button"; @@ -4827,7 +4828,8 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdIsJustActivated = (g.ActiveId != id); if (g.ActiveIdIsJustActivated) { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : ""); + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() 0x%08X in \"%s\"%*s(previously 0x%08X in \"%s\")\n", id, window ? window->Name : "", + ImMax(0, 20 - (int)(window ? strlen(window->Name) : 0)), "", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : ""); g.ActiveIdTimer = 0.0f; g.ActiveIdHasBeenPressedBefore = false; g.ActiveIdHasBeenEditedBefore = false; @@ -5953,7 +5955,7 @@ void ImGui::NewFrame() g.CurrentWindowStack.resize(0); g.BeginPopupStack.resize(0); g.ItemFlagsStack.resize(0); - g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags + g.ItemFlagsStack.push_back(ImGuiItemFlags_Default_); // Default flags g.CurrentItemFlags = g.ItemFlagsStack.back(); g.GroupStack.resize(0); @@ -10989,15 +10991,21 @@ void ImGui::UpdateMouseWheel() LockWheelingWindow(NULL, 0.0f); } - ImVec2 wheel; - wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f; - wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f; - - //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; if (!mouse_window || mouse_window->Collapsed) return; + ImGuiID owner_id = mouse_window->ID; + ImVec2 wheel; + wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, owner_id) ? g.IO.MouseWheelH : 0.0f; + wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, owner_id) ? g.IO.MouseWheel : 0.0f; + //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); + if (g.WheelingWindow != NULL) + { + SetKeyOwner(ImGuiKey_MouseWheelX, owner_id); + SetKeyOwner(ImGuiKey_MouseWheelY, owner_id); + } + // Zoom / Scale window // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) @@ -11306,6 +11314,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) // - SetKeyOwner(..., None) : clears owner // - SetKeyOwner(..., Any, !Lock) : illegal (assert) // - SetKeyOwner(..., Any or None, Lock) : set lock +// Ownership is automatically released on the frame after a release, see code in UpdateKeyboardInputs(). void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; @@ -11332,30 +11341,34 @@ void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, I if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); } } -// This is more or less equivalent to: +// This is more or less equivalent to a fancier version of: // if (IsItemHovered() || IsItemActive()) // SetKeyOwner(key, GetItemID()); // Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times. // More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition. // Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority. -void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) +bool ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; ImGuiID id = g.LastItemData.ID; if (id == 0 || (g.HoveredId != id && g.ActiveId != id)) - return; + return false; if ((flags & ImGuiInputFlags_CondMask_) == 0) flags |= ImGuiInputFlags_CondDefault_; if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive))) { IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function! + if (!TestKeyOwner(key, id)) + return false; SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_); + return true; } + return false; } -void ImGui::SetItemKeyOwner(ImGuiKey key) +bool ImGui::SetItemKeyOwner(ImGuiKey key) { - SetItemKeyOwner(key, ImGuiInputFlags_None); + return SetItemKeyOwner(key, ImGuiInputFlags_None); } // This is the only public API until we expose owner_id versions of the API as replacements. @@ -13156,6 +13169,17 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext return false; } + // As we bypass BeginChild(), set ImGuiChildFlags_AlwaysAutoResize as it is checked independently from ImGuiWindowFlags_AlwaysAutoResize for now (see #9355) + // Ideally we should remove setting ImGuiWindowFlags_AlwaysAutoResize in BeginChild(). + if ((extra_window_flags & ImGuiWindowFlags_ChildWindow) && (extra_window_flags & ImGuiWindowFlags_AlwaysAutoResize)) + { + if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) + g.NextWindowData.ChildFlags |= ImGuiChildFlags_AlwaysAutoResize; + else + g.NextWindowData.ChildFlags = ImGuiChildFlags_AlwaysAutoResize; + g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags; + } + char name[128]; IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu); ImFormatString(name, IM_COUNTOF(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth @@ -24318,7 +24342,7 @@ void ImGui::ShowFontSelector(const char* label) "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n" "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" "- Read FAQ and docs/FONTS.md for more details.\n" - "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); + "- Legacy backend: if you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); } #endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS) diff --git a/imgui.h b/imgui.h index d9e7d02e2..86cb07b4d 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.8 WIP" -#define IMGUI_VERSION_NUM 19276 +#define IMGUI_VERSION "1.92.8" +#define IMGUI_VERSION_NUM 19280 #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. @@ -1128,10 +1128,11 @@ namespace ImGui // Inputs Utilities: Key/Input Ownership [BETA] // - One common use case would be to allow your items to disable standard inputs behaviors such // as Tab or Alt key handling, Mouse Wheel scrolling, etc. - // e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling. + // e.g. `Button(...); if (SetItemKeyOwner(ImGuiKey_MouseWheelY)) { ... }` to make hovering/activating a button disable wheel for scrolling. // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them. + // - The return value of SetItemKeyOwner() says if ownership has been requested for the item, which is a shortcut to calling yet non-public TestKeyOwner() function. // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version. - IMGUI_API void SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Return true when ownership has been set. Roughly equivalent to 'if (TestKeyOwner(key, GetItemID()) && (IsItemHovered() || IsItemActive())) { SetKeyOwner(key, GetItemID());'. // Inputs Utilities: Mouse // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. @@ -1838,6 +1839,7 @@ enum ImGuiCol_ ImGuiCol_ScrollbarGrabHovered, ImGuiCol_ScrollbarGrabActive, ImGuiCol_CheckMark, // Checkbox tick and RadioButton circle + ImGuiCol_CheckboxSelectedBg, // Checkbox background when Selected, otherwise use FrameBg ImGuiCol_SliderGrab, ImGuiCol_SliderGrabActive, ImGuiCol_Button, @@ -3667,6 +3669,7 @@ struct ImTextureData bool WantDestroyNextFrame; // rw - // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame. // Functions + // - If GetPixels() functions asserts while being called by your render loop, it could be caused by calling ImFontAtlas::Clear() instead of ClearFonts()? ImTextureData() { memset((void*)this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; } ~ImTextureData() { DestroyPixels(); } IMGUI_API void Create(ImTextureFormat format, int w, int h); @@ -3822,13 +3825,13 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void RemoveFont(ImFont* font); - IMGUI_API void Clear(); // Clear everything (input fonts, output glyphs/textures). + IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame! + IMGUI_API void ClearFonts(); // Clear input+output font data/glyphs. You can call this mid-frame if you load new fonts afterwards! IMGUI_API void CompactCache(); // Compact cached glyphs and texture. IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime. // As we are transitioning toward a new font system, we expect to obsolete those soon: IMGUI_API void ClearInputData(); // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. - IMGUI_API void ClearFonts(); // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates). IMGUI_API void ClearTexData(); // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5fccfcb0d..3941966a1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (demo code) // Help: @@ -73,6 +73,7 @@ Index of this file: // [SECTION] Demo Window / ShowDemoWindow() // [SECTION] DemoWindowMenuBar() // [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos) +// [SECTION] Helpers: ExampleImageViewer // [SECTION] DemoWindowWidgetsBasic() // [SECTION] DemoWindowWidgetsBullets() // [SECTION] DemoWindowWidgetsCollapsingHeaders() @@ -108,6 +109,7 @@ Index of this file: // [SECTION] User Guide / ShowUserGuide() // [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar() // [SECTION] Example App: Debug Console / ShowExampleAppConsole() +// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer() // [SECTION] Example App: Debug Log / ShowExampleAppLog() // [SECTION] Example App: Simple Layout / ShowExampleAppLayout() // [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor() @@ -240,6 +242,7 @@ static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleAppDockSpace(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); +static void ShowExampleAppImageViewer(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data); @@ -321,6 +324,7 @@ struct ImGuiDemoWindowData bool ShowAppCustomRendering = false; bool ShowAppDocuments = false; bool ShowAppDockSpace = false; + bool ShowAppImageViewer = false; bool ShowAppLog = false; bool ShowAppLayout = false; bool ShowAppPropertyEditor = false; @@ -367,6 +371,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); } if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); } if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); } + if (demo_data.ShowAppImageViewer) { ShowExampleAppImageViewer(&demo_data.ShowAppImageViewer); } if (demo_data.ShowAppLog) { ShowExampleAppLog(&demo_data.ShowAppLog); } if (demo_data.ShowAppLayout) { ShowExampleAppLayout(&demo_data.ShowAppLayout); } if (demo_data.ShowAppPropertyEditor) { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); } @@ -748,6 +753,7 @@ static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data) ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering); ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments); ImGui::MenuItem("Dockspace", NULL, &demo_data->ShowAppDockSpace); + ImGui::MenuItem("Image Viewer", NULL, &demo_data->ShowAppImageViewer); ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog); ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor); ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout); @@ -779,7 +785,7 @@ static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data) ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts); ImGui::EndDisabled(); ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert); - ImGui::TextDisabled("(see Demo->Configuration for details & more)"); + ImGui::TextDisabled("(see Demo->Configuration for more)"); ImGui::EndMenu(); } ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools); @@ -896,6 +902,87 @@ static ExampleTreeNode* ExampleTree_CreateDemoTree() return node_L0; } +//----------------------------------------------------------------------------- +// [SECTION] Helpers: ExampleImageViewer +//----------------------------------------------------------------------------- + +struct ExampleImageViewerData +{ + ImU32 ImageBgColor = IM_COL32(100, 100, 100, 255); + ImU32 GridColor = IM_COL32(255, 255, 255, 100); + bool GridEnabled = true; + bool ViewReset = true; + ImVec2 ViewOffset; // in image space + float Zoom = 10.0f; + float ZoomMin = 1.0f; + float ZoomMax = 10000.0f; +}; + +static void ExampleImageViewer_DrawOptions(ExampleImageViewerData* data) +{ + ImGui::SetNextItemShortcut(ImGuiKey_G, ImGuiInputFlags_Tooltip); // | ImGuiInputFlags_RouteGlobal + ImGui::Checkbox("Grid", &data->GridEnabled); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f); + float zoom_100 = data->Zoom * 100.0f; + if (ImGui::DragFloat("##Zoom", &zoom_100, 5.0f, data->ZoomMin * 100.0f, data->ZoomMax * 100.0f, "%.0f%%", ImGuiSliderFlags_AlwaysClamp)) + data->Zoom = zoom_100 / 100.0f; +} + +static void ExampleImageViewer_DrawCanvas(ExampleImageViewerData* data, ImVec2 canvas_size, ImTextureRef image_tex_ref, int image_w, int image_h) +{ + ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + IM_ASSERT(canvas_size.x >= 0.0f && canvas_size.y >= 0.0f); + + // Layout canvas + ImGui::InvisibleButton("##Canvas", canvas_size); + ImVec2 canvas_min = ImGui::GetItemRectMin(); + ImVec2 canvas_max = ImGui::GetItemRectMax(); + + if (data->ViewReset) + data->ViewOffset = ImVec2((canvas_size.x * 0.5f / data->Zoom) - 0.5f, (canvas_size.y * 0.5f / data->Zoom) - 0.5f); // Add half a pixel padding + data->ViewReset = false; + + // Handle inputs + if (ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY)) + if (io.MouseWheel != 0.0f) + data->Zoom = IM_CLAMP(data->Zoom * (1.0f + io.MouseWheel * 0.10f), data->ZoomMin, data->ZoomMax); + float zoom = data->Zoom; // (float)(int)ViewZoom; + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) + { + data->ViewOffset.x -= io.MouseDelta.x / zoom; + data->ViewOffset.y -= io.MouseDelta.y / zoom; + } + + // Display image + ImVec2 image_min, image_max; + image_min.x = (float)(int)((canvas_min.x - (data->ViewOffset.x * zoom)) + (canvas_size.x * 0.5f)); + image_min.y = (float)(int)((canvas_min.y - (data->ViewOffset.y * zoom)) + (canvas_size.y * 0.5f)); + image_max.x = (float)(int)(image_min.x + image_w * zoom); + image_max.y = (float)(int)(image_min.y + image_h * zoom); + draw_list->AddRect(ImVec2(canvas_min.x - 1.0f, canvas_min.y - 1.0f), ImVec2(canvas_max.x + 1.0f, canvas_max.y + 1.0f), IM_COL32(255, 255, 255, 255)); + draw_list->PushClipRect(canvas_min, canvas_max, true); + draw_list->AddRectFilled(image_min, image_max, data->ImageBgColor); + if (platform_io.DrawCallback_SetSamplerNearest != NULL) + draw_list->AddCallback(platform_io.DrawCallback_SetSamplerNearest); + draw_list->AddImage(image_tex_ref, image_min, image_max); + if (platform_io.DrawCallback_SetSamplerLinear != NULL) + draw_list->AddCallback(ImGui::GetPlatformIO().DrawCallback_SetSamplerLinear); + + // Display grid lines for visible pixels + if (data->GridEnabled && zoom > 6.0f) + { + const float step = (float)zoom; + for (int px = (int)((canvas_min.x - image_min.x) / step); px <= (int)((canvas_max.x - image_min.x) / step); px++) + draw_list->AddLineV(image_min.x + px * step, canvas_min.y, canvas_max.y, data->GridColor, 1.0f); + for (int py = (int)((canvas_min.y - image_min.y) / step); py <= (int)((canvas_max.y - image_min.y) / step); py++) + draw_list->AddLineH(canvas_min.x, canvas_max.x, image_min.y + py * step, data->GridColor, 1.0f); + } + draw_list->PopClipRect(); +} + //----------------------------------------------------------------------------- // [SECTION] DemoWindowWidgetsBasic() //----------------------------------------------------------------------------- @@ -1873,40 +1960,29 @@ static void DemoWindowWidgetsImages() // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples // Grab the current texture identifier used by the font atlas. - ImTextureRef my_tex_id = io.Fonts->TexRef; + ImFontAtlas* atlas = io.Fonts; + ImTextureRef my_tex_id = atlas->TexRef; + float my_tex_w = (float)atlas->TexData->Width; // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it. + float my_tex_h = (float)atlas->TexData->Height; + ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); - // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it. - float my_tex_w = (float)io.Fonts->TexData->Width; - float my_tex_h = (float)io.Fonts->TexData->Height; + // Basic drawing + ImGui::SeparatorText("Image()/ImageWithBg() function"); + ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left + ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right + ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize)); + ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); + ImGui::PopStyleVar(); - { - ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left - ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right - ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize)); - ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - if (ImGui::BeginItemTooltip()) - { - float region_sz = 32.0f; - float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; - float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; - float zoom = 4.0f; - if (region_x < 0.0f) { region_x = 0.0f; } - else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; } - if (region_y < 0.0f) { region_y = 0.0f; } - else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; } - ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); - ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); - ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); - ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); - ImGui::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - ImGui::EndTooltip(); - } - ImGui::PopStyleVar(); - } + // Fancy widget + ImGui::SeparatorText("Interactive Image Viewer"); + static ExampleImageViewerData image_viewer; + ImVec2 canvas_size(ImGui::GetContentRegionAvail().x, my_tex_h * 2.0f); + ExampleImageViewer_DrawOptions(&image_viewer); + ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, my_tex_id, (int)my_tex_w, (int)my_tex_h); IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons"); + ImGui::SeparatorText("Textured Buttons"); ImGui::TextWrapped("And now some textured buttons.."); static int pressed_count = 0; for (int i = 0; i < 8; i++) @@ -8941,7 +9017,7 @@ static void ShowExampleMenuFile() IMGUI_DEMO_MARKER("Examples/Menu/Options"); static bool enabled = true; ImGui::MenuItem("Enabled", "", &enabled); - ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders); + ImGui::BeginChild("child", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 5.0f), ImGuiChildFlags_Borders); for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i); ImGui::EndChild(); @@ -9353,6 +9429,28 @@ static void ShowExampleAppConsole(bool* p_open) console.Draw("Example: Console", p_open); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Image Viewer / ShowExampleAppImageViewer() +//----------------------------------------------------------------------------- + +static void ShowExampleAppImageViewer(bool* p_open) +{ + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + ImTextureRef tex_ref = atlas->TexRef; // We don't have access to other textures in this demo! + int tex_w = atlas->TexData->Width; + int tex_h = atlas->TexData->Height; + if (ImGui::Begin("Example: Image Viewer", p_open)) + { + static ExampleImageViewerData image_viewer; + ExampleImageViewer_DrawOptions(&image_viewer); + ImVec2 canvas_size = ImGui::GetContentRegionAvail(); + ImVec2 canvas_min_size = ImGui::IsWindowAppearing() ? ImVec2(3.0f * tex_w, 4.0f * tex_h) : ImVec2(1.0f, 1.0f); + canvas_size = ImVec2(IM_MAX(canvas_size.x, canvas_min_size.x), IM_MAX(canvas_size.y, canvas_min_size.y)); + ExampleImageViewer_DrawCanvas(&image_viewer, canvas_size, tex_ref, tex_w, tex_h); + } + ImGui::End(); +} + //----------------------------------------------------------------------------- // [SECTION] Example App: Debug Log / ShowExampleAppLog() //----------------------------------------------------------------------------- diff --git a/imgui_draw.cpp b/imgui_draw.cpp index f39b2b3aa..b5a93b10b 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (drawing and font code) /* @@ -208,6 +208,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_CheckboxSelectedBg] = ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBgHovered], 0.65f); colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); @@ -277,6 +278,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); + colors[ImGuiCol_CheckboxSelectedBg] = ImLerp(colors[ImGuiCol_FrameBg], colors[ImGuiCol_FrameBgActive], 0.65f); colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); colors[ImGuiCol_Button] = ImVec4(0.35f, 0.40f, 0.61f, 0.62f); @@ -347,6 +349,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); + colors[ImGuiCol_CheckboxSelectedBg] = ImVec4(0.95f, 0.97f, 1.00f, 1.00f); colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.46f, 0.54f, 0.80f, 0.60f); colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); @@ -2522,10 +2525,11 @@ void ImTextureData::DestroyPixels() // - Default texture data encoded in ASCII // - ImFontAtlas() // - ImFontAtlas::Clear() -// - ImFontAtlas::CompactCache() +// - ImFontAtlas::ClearFonts() // - ImFontAtlas::ClearInputData() // - ImFontAtlas::ClearTexData() -// - ImFontAtlas::ClearFonts() +// - ImFontAtlas::CompactCache() +// - ImFontAtlas::SetFontLoader() //----------------------------------------------------------------------------- // - ImFontAtlasUpdateNewFrame() // - ImFontAtlasTextureBlockConvert() @@ -2686,7 +2690,9 @@ ImFontAtlas::~ImFontAtlas() TexData = NULL; } -// If you call this mid-frame, you would need to add new font and bind them! +// You probably should not call this directly. It is not well specified. +// If you want to replace all your fonts mid-frame, most likely you should instead call ClearFonts() then load the new fonts. +// Calling this mid-frame will discard the CPU-side copy of the texture data which is generally unreliable as you may have textures queued for creation or updates. void ImFontAtlas::Clear() { bool backup_renderer_has_textures = RendererHasTextures; @@ -2696,20 +2702,27 @@ void ImFontAtlas::Clear() RendererHasTextures = backup_renderer_has_textures; } -void ImFontAtlas::CompactCache() +void ImFontAtlas::ClearFonts() { - ImFontAtlasTextureCompact(this); -} - -void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) -{ - ImFontAtlasBuildSetupFontLoader(this, font_loader); + // FIXME-NEWATLAS: Illegal to remove currently bound font. + IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); + for (ImFont* font : Fonts) + ImFontAtlasBuildNotifySetFont(this, font, NULL); + ImFontAtlasBuildDestroy(this); + ClearInputData(); + Fonts.clear_delete(); + TexIsBuilt = false; + for (ImDrawListSharedData* shared_data : DrawListSharedDatas) + if (shared_data->FontAtlas == this) + { + shared_data->Font = NULL; + shared_data->FontScale = shared_data->FontSize = 0.0f; + } } void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - for (ImFont* font : Fonts) ImFontAtlasFontDestroyOutput(this, font); for (ImFontConfig& font_cfg : Sources) @@ -2733,22 +2746,14 @@ void ImFontAtlas::ClearTexData() //Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it. } -void ImFontAtlas::ClearFonts() +void ImFontAtlas::CompactCache() { - // FIXME-NEWATLAS: Illegal to remove currently bound font. - IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!"); - for (ImFont* font : Fonts) - ImFontAtlasBuildNotifySetFont(this, font, NULL); - ImFontAtlasBuildDestroy(this); - ClearInputData(); - Fonts.clear_delete(); - TexIsBuilt = false; - for (ImDrawListSharedData* shared_data : DrawListSharedDatas) - if (shared_data->FontAtlas == this) - { - shared_data->Font = NULL; - shared_data->FontScale = shared_data->FontSize = 0.0f; - } + ImFontAtlasTextureCompact(this); +} + +void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader) +{ + ImFontAtlasBuildSetupFontLoader(this, font_loader); } static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas) diff --git a/imgui_internal.h b/imgui_internal.h index a17396f0f..e7a95476c 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (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. @@ -1222,6 +1222,7 @@ struct IMGUI_API ImGuiMenuColumns }; // Internal temporary state for deactivating InputText() instances. +// Store as part of ImGuiDeactivatedItemData? struct IMGUI_API ImGuiInputTextDeactivatedState { ImGuiID ID; // widget id owning the text state (which just got deactivated) @@ -1475,6 +1476,7 @@ struct ImGuiPtrOrIndex }; // Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions +// Also see ImGuiInputTextDeactivatedState which is an extension for this for InputText() struct ImGuiDeactivatedItemData { ImGuiID ID; @@ -3673,7 +3675,7 @@ namespace ImGui IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); - IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags); IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 352ed62e6..135410a0d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b06f4c88a..c255b5d6a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.8 WIP +// dear imgui, v1.92.8 // (widgets code) /* @@ -1295,8 +1295,9 @@ bool ImGui::Checkbox(const char* label, bool* v) if (is_visible) { RenderNavCursor(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : (mixed_value || checked) ? ImGuiCol_CheckboxSelectedBg : ImGuiCol_FrameBg); ImU32 check_col = GetColorU32(ImGuiCol_CheckMark); + RenderFrame(check_bb.Min, check_bb.Max, bg_col, true, style.FrameRounding); if (mixed_value) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) @@ -3578,7 +3579,8 @@ bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, // - ImParseFormatSanitizeForPrinting() [Internal] // - ImParseFormatSanitizeForScanning() [Internal] // - ImParseFormatPrecision() [Internal] -// - TempInputTextScalar() [Internal] +// - TempInputText() [Internal] +// - TempInputScalar() [Internal] // - InputScalar() // - InputScalarN() // - InputFloat() @@ -4589,6 +4591,7 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) ImGuiInputTextState* state = &g.InputTextState; if (id == 0 || state->ID != id) return; + //IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id); g.InputTextDeactivatedState.ID = state->ID; if (state->Flags & ImGuiInputTextFlags_ReadOnly) { @@ -4904,7 +4907,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } const bool is_osx = io.ConfigMacOSXBehaviors; - if (g.ActiveId != id && init_make_active) + if (init_make_active && g.ActiveId != id) { IM_ASSERT(state && state->ID == id); SetActiveID(id, window); @@ -9402,8 +9405,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) bool pressed; - // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups; + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoAutoClosePopups | (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnClick; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -9441,6 +9443,14 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (!enabled) EndDisabled(); + // Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394) + // Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here. + if (g.ActiveId == id && g.HoveredId != id && g.ActiveIdSource == ImGuiInputSource_Mouse && IsMouseDragging(0)) + { + ClearActiveID(); + SetKeyOwner(ImGuiKey_MouseLeft, ImGuiKeyOwner_NoOwner); + } + const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav; if (menuset_is_open) PopItemFlag(); @@ -9521,6 +9531,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); PopID(); + if (g.ActiveId == id && want_open) + g.ActiveIdNoClearOnFocusLoss = true; + if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame. @@ -9613,7 +9626,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut BeginDisabled(); // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; + const ImGuiSelectableFlags selectable_flags = (ImGuiSelectableFlags)ImGuiSelectableFlags_SelectOnRelease | (ImGuiSelectableFlags)ImGuiSelectableFlags_SetNavIdOnHover; ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -9656,6 +9669,17 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut RenderCheckMark(window->DrawList, text_pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } } + + // Once dragged, release ActiveId + key ownership. This is to allow the idiom of mouse down a menu, dragging elsewhere, up on some other MenuItem(). (#8233, #9394) + // Could move logic into lower-level ImGuiButtonFlags_AutoReleaseActiveId + ImGuiButtonFlags_AutoReleaseKeyOwner? Easier once we get rid of the Selectable() middle-man here. + const ImGuiID id = g.LastItemData.ID; + if (g.ActiveId == id && g.HoveredId != id && g.ActiveIdSource == ImGuiInputSource_Mouse && IsMouseDragging(0)) + { + ClearActiveID(); + SetKeyOwner(ImGuiKey_MouseLeft, ImGuiKeyOwner_NoOwner); + } + + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); if (!enabled) EndDisabled();