diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 783615aec..92d41dac1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,8 @@ jobs: steps: - uses: actions/checkout@v4 + # The VulkanSDK libs for Windows is manually generated using build_windows_vulkan_libs.ps1 + attached to issue #8925. + # (we have a .yml workflow in commit history if it becomes ever useful to create this on CI too) - name: Install Dependencies shell: powershell run: | @@ -35,20 +37,9 @@ jobs: Expand-Archive -Path SDL3-devel-3.2.18-VC.zip echo "SDL3_DIR=$(pwd)\SDL3-devel-3.2.18-VC\SDL3-3.2.18\" >>${env:GITHUB_ENV} - # VulkanSDK (retrieve minimal bits of the SDK from git) - $vulkanVersion = "1.4.326" - # 1. Get the vulkan headers, we will treat that folder as the sdk folder to avoid having to copy headers around - Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v$($vulkanVersion).zip" -OutFile Vulkan-Headers-$($vulkanVersion).zip - Expand-Archive -Path Vulkan-Headers-$($vulkanVersion).zip - echo "VULKAN_SDK=$(pwd)\Vulkan-Headers-$($vulkanVersion)\Vulkan-Headers-$($vulkanVersion)" >>${env:GITHUB_ENV} - # 2. Get and build the vulkan loader source code (UPDATE_DEPS=On will make it automatically fetch its dependencies) - Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/v$($vulkanVersion).zip" -OutFile Vulkan-Loader-$($vulkanVersion).zip - Expand-Archive -Path Vulkan-Loader-$($vulkanVersion).zip - cmake -S Vulkan-Loader-$($vulkanVersion)\Vulkan-Loader-$($vulkanVersion) -B VulkanLoader-build -D UPDATE_DEPS=On - cmake --build VulkanLoader-build - # 3. Copy the built lib/dll to the expected place - mkdir Vulkan-Headers-$($vulkanVersion)\Vulkan-Headers-$($vulkanVersion)\Lib - copy VulkanLoader-build\loader\Debug\vulkan-1.* Vulkan-Headers-$($vulkanVersion)\Vulkan-Headers-$($vulkanVersion)\Lib\ + Invoke-WebRequest -Uri "https://github.com/user-attachments/files/22464296/vulkan_windows_libs_1.4.326.zip" -OutFile vulkan_windows_libs_1.4.326.zip + Expand-Archive -Path vulkan_windows_libs_1.4.326.zip + echo "VULKAN_SDK=$(pwd)\vulkan_windows_libs_1.4.326\" >>${env:GITHUB_ENV} - name: Fix Projects shell: powershell diff --git a/.github/workflows/build_windows_vulkan_libs.ps1 b/.github/workflows/build_windows_vulkan_libs.ps1 new file mode 100644 index 000000000..6658f5b5b --- /dev/null +++ b/.github/workflows/build_windows_vulkan_libs.ps1 @@ -0,0 +1,38 @@ +# This is current meant to be run manually, occasionally: +# - Run then zip the contents of vulkanArtifact/ into e.g. vulkan_windows_libs_1.4.326 +# - Upload as an attachment to https://github.com/ocornut/imgui/pull/8925 then change filename in build.yml +# - There is a build_windows_vulkan_libs.yml in commit history that we removed thinking this is run so rarely we don't need to pollute CI UI with it. + +# Set default vulkan version if none provided +if (-not $env:VULKAN_TAG) { $env:VULKAN_TAG = "1.4.326" } + +# Create output folder +mkdir vulkanArtifact + +# Download Vulkan Headers + +Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v$($env:VULKAN_TAG).zip" -OutFile Vulkan-Headers-$($env:VULKAN_TAG).zip +Expand-Archive -Path Vulkan-Headers-$($env:VULKAN_TAG).zip + +# Copy Vulkan Headers to artifact folder + +cp -R Vulkan-Headers-$($env:VULKAN_TAG)\Vulkan-Headers-$($env:VULKAN_TAG)\include vulkanArtifact\Include + +# Download Vulkan Loader + +Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/v$($env:VULKAN_TAG).zip" -OutFile Vulkan-Loader-$($env:VULKAN_TAG).zip +Expand-Archive -Path Vulkan-Loader-$($env:VULKAN_TAG).zip + +# Build Vulkan Loader x64 + +cmake -S Vulkan-Loader-$($env:VULKAN_TAG)\Vulkan-Loader-$($env:VULKAN_TAG) -B VulkanLoader-build64 -D UPDATE_DEPS=On -A x64 +cmake --build VulkanLoader-build64 +mkdir vulkanArtifact\Lib +copy VulkanLoader-build64\loader\Debug\vulkan-1.lib vulkanArtifact\Lib + +# Build Vulkan Loader win32 + +cmake -S Vulkan-Loader-$($env:VULKAN_TAG)\Vulkan-Loader-$($env:VULKAN_TAG) -B VulkanLoader-build32 -D UPDATE_DEPS=On -A Win32 +cmake --build VulkanLoader-build32 +mkdir vulkanArtifact\Lib32 +copy VulkanLoader-build32\loader\Debug\vulkan-1.lib vulkanArtifact\Lib32 diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 4b09c63b7..7e13971e5 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -997,7 +997,6 @@ void ImGui_ImplDX12_Shutdown() main_viewport->RendererUserData = nullptr; } - // Clean up windows and device objects ImGui_ImplDX12_ShutdownMultiViewportSupport(); ImGui_ImplDX12_InvalidateDeviceObjects(); diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 35f950f2d..905f86488 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -822,7 +822,6 @@ void ImGui_ImplGlfw_Shutdown() ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplGlfw_ShutdownMultiViewportSupport(); - if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h index a69ac89d7..2c80cc598 100644 --- a/backends/imgui_impl_opengl3_loader.h +++ b/backends/imgui_impl_opengl3_loader.h @@ -714,7 +714,11 @@ static void close_libgl(void) static int is_library_loaded(const char* name, void** lib) { +#if defined(__HAIKU__) + *lib = NULL; // no support for RTLD_NOLOAD on Haiku. +#else *lib = dlopen(name, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); +#endif return *lib != NULL; } diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index d50b06d1c..ed2b1ce1d 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -26,6 +26,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-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_MOUSEMOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786) // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. // 2025-09-15: Content Scales are always reported as 1.0 on Wayland. (#8921) // 2025-07-08: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return 1.0f on Emscripten and Android platforms, matching macOS logic. (#8742, #8733) @@ -706,7 +707,6 @@ void ImGui_ImplSDL2_Shutdown() ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplSDL2_ShutdownMultiViewportSupport(); - if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -759,13 +759,15 @@ static void ImGui_ImplSDL2_UpdateMouseData() SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); } - // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured) + // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_MOUSEMOTION already provides this when hovered or captured) + SDL_Window* hovered_window = SDL_GetMouseFocus(); const bool is_relative_mouse_mode = SDL_GetRelativeMouseMode() != 0; - if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode) + if (hovered_window == NULL && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode) { // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) - int mouse_x, mouse_y, window_x, window_y; + int mouse_x, mouse_y; + int window_x, window_y; SDL_GetGlobalMouseState(&mouse_x, &mouse_y); if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) { diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index aae620ea5..f26092c31 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -24,6 +24,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-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_EVENT_MOUSE_MOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786) // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. // 2025-09-15: Use SDL_GetWindowDisplayScale() on Mac to output DisplayFrameBufferScale. The function is more reliable during resolution changes e.g. going fullscreen. (#8703, #4414) // 2025-06-27: IME: avoid calling SDL_StartTextInput() again if already active. (#8727) @@ -666,7 +667,6 @@ void ImGui_ImplSDL3_Shutdown() ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGui_ImplSDL3_ShutdownMultiViewportSupport(); - if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -718,9 +718,10 @@ static void ImGui_ImplSDL3_UpdateMouseData() SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); } - // (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) + // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) + SDL_Window* hovered_window = SDL_GetMouseFocus(); const bool is_relative_mouse_mode = SDL_GetWindowRelativeMouseMode(bd->Window); - if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode) + if (hovered_window == NULL && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode) { // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) @@ -733,7 +734,7 @@ static void ImGui_ImplSDL3_UpdateMouseData() mouse_x -= window_x; mouse_y -= window_y; } - io.AddMousePosEvent((float)mouse_x, (float)mouse_y); + io.AddMousePosEvent(mouse_x, mouse_y); } } diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 3b33169da..4312e4ff4 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.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-09-23: Inputs: Minor optimization not submitting gamepad input if packet number has not changed. // 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown. // 2025-06-02: [Docking] WM_DPICHANGED also apply io.ConfigDpiScaleViewports for main viewport instead of letting it be done by application code. // 2025-04-30: Inputs: Fixed an issue where externally losing mouse capture (due to e.g. focus loss) would fail to claim it again the next subsequent click. (#8594) @@ -138,6 +139,7 @@ struct ImGui_ImplWin32_Data HMODULE XInputDLL; PFN_XInputGetCapabilities XInputGetCapabilities; PFN_XInputGetState XInputGetState; + DWORD XInputPacketNumber; #endif ImGui_ImplWin32_Data() { memset((void*)this, 0, sizeof(*this)); } @@ -418,6 +420,9 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io) if (!bd->HasGamepad || bd->XInputGetState == nullptr || bd->XInputGetState(0, &xinput_state) != ERROR_SUCCESS) return; io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + if (bd->XInputPacketNumber != 0 && bd->XInputPacketNumber == xinput_state.dwPacketNumber) + return; + bd->XInputPacketNumber = xinput_state.dwPacketNumber; #define IM_SATURATE(V) (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) #define MAP_BUTTON(KEY_NO, BUTTON_ENUM) { io.AddKeyEvent(KEY_NO, (gamepad.wButtons & BUTTON_ENUM) != 0); } diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 99b364ebd..6d7a8f98e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -48,11 +48,29 @@ Breaking Changes: 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. - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) +- 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] +- 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) +- Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support + `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] +- 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) + - 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] +- 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] Docking+Viewports Branch: diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 038bd5858..f61297fd1 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -149,8 +149,12 @@ SDL2 (Win32, Mac, Linux, etc.) + Vulkan example.
This is quite long and tedious, because: Vulkan.
For this example, the main.cpp file exceptionally use helpers function from imgui_impl_vulkan.h/cpp. +[example_sdl3_directx11/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_directx11/)
+SDL3 + DirectX11 examples, Windows only.
+= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_dx11.cpp
+ [example_sdl3_metal/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_metal/)
-SDL3 + Metal example (Mac).
+SDL3 + Metal example, Mac only.
= main.cpp + imgui_impl_sdl3.cpp + imgui_impl_metal.mm
[example_sdl3_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl3_opengl3/)
diff --git a/examples/example_sdl2_directx11/main.cpp b/examples/example_sdl2_directx11/main.cpp index be6a3d794..298fcaaf7 100644 --- a/examples/example_sdl2_directx11/main.cpp +++ b/examples/example_sdl2_directx11/main.cpp @@ -250,7 +250,10 @@ bool CreateDeviceD3D(HWND hWnd) //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; D3D_FEATURE_LEVEL featureLevel; const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; - if (D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK) + HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available. + res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res != S_OK) return false; CreateRenderTarget(); diff --git a/examples/example_sdl3_directx11/build_win32.bat b/examples/example_sdl3_directx11/build_win32.bat new file mode 100644 index 000000000..b84b01f1a --- /dev/null +++ b/examples/example_sdl3_directx11/build_win32.bat @@ -0,0 +1,8 @@ +@REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. +@set OUT_DIR=Debug +@set OUT_EXE=example_sdl3_directx11 +@set INCLUDES=/I..\.. /I..\..\backends /I%SDL3_DIR%\include /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" +@set SOURCES=main.cpp ..\..\backends\imgui_impl_sdl3.cpp ..\..\backends\imgui_impl_dx11.cpp ..\..\imgui*.cpp +@set LIBS=/LIBPATH:%SDL3_DIR%\lib\x86 SDL3.lib /LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib shell32.lib +mkdir %OUT_DIR% +cl /nologo /Zi /MD /utf-8 %INCLUDES% %SOURCES% /Fe%OUT_DIR%/%OUT_EXE%.exe /Fo%OUT_DIR%/ /link %LIBS% /subsystem:console diff --git a/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj new file mode 100644 index 000000000..340901c3d --- /dev/null +++ b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {009DAC16-1A9C-47BE-9770-A30A046E8090} + example_sdl3_directx11 + 8.1 + example_sdl3_directx11 + + + + Application + true + MultiByte + v140 + + + Application + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + Application + false + true + MultiByte + v140 + + + + + + + + + + + + + + + + + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + $(ProjectDir)$(Configuration)\ + $(ProjectDir)$(Configuration)\ + $(IncludePath) + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + /utf-8 %(AdditionalOptions) + + + true + %SDL3_DIR%\lib\x86;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + Disabled + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + /utf-8 %(AdditionalOptions) + + + true + %SDL3_DIR%\lib\x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + msvcrt.lib + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + false + /utf-8 %(AdditionalOptions) + + + true + true + true + %SDL3_DIR%\lib\x86;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + + + + + + + Level4 + MaxSpeed + true + true + ..\..;..\..\backends;%SDL3_DIR%\include;$(VcpkgCurrentInstalledDir)include;%(AdditionalIncludeDirectories) + false + /utf-8 %(AdditionalOptions) + + + true + true + true + %SDL3_DIR%\lib\x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories) + SDL3.lib;d3d11.lib;d3dcompiler.lib;dxgi.lib;%(AdditionalDependencies) + Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj.filters b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj.filters new file mode 100644 index 000000000..0579b7a64 --- /dev/null +++ b/examples/example_sdl3_directx11/example_sdl3_directx11.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {0587d7a3-f2ce-4d56-b84f-a0005d3bfce6} + + + {08e36723-ce4f-4cff-9662-c40801cf1acf} + + + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + imgui + + + sources + + + imgui + + + imgui + + + imgui + + + imgui + + + sources + + + sources + + + + + + imgui + + + imgui + + + \ No newline at end of file diff --git a/examples/example_sdl3_directx11/main.cpp b/examples/example_sdl3_directx11/main.cpp new file mode 100644 index 000000000..bc8889b8c --- /dev/null +++ b/examples/example_sdl3_directx11/main.cpp @@ -0,0 +1,257 @@ +// Dear ImGui: standalone example application for SDL3 + DirectX 11 +// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#include "imgui.h" +#include "imgui_impl_sdl3.h" +#include "imgui_impl_dx11.h" +#include +#include // printf +#include + +// Data +static ID3D11Device* g_pd3dDevice = nullptr; +static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr; +static IDXGISwapChain* g_pSwapChain = nullptr; +static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; + +// Forward declarations of helper functions +bool CreateDeviceD3D(HWND hWnd); +void CleanupDeviceD3D(); +void CreateRenderTarget(); +void CleanupRenderTarget(); + +// Main code +int main(int, char**) +{ + // Setup SDL + // [If using SDL_MAIN_USE_CALLBACKS: all code below until the main loop starts would likely be your SDL_AppInit() function] + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return -1; + } + + // Setup window + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+DirectX11 example", (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return -1; + } + + SDL_PropertiesID props = SDL_GetWindowProperties(window); + HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); + + // Initialize Direct3D + if (!CreateDeviceD3D(hwnd)) + { + CleanupDeviceD3D(); + return -1; + } + + SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + SDL_ShowWindow(window); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + 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_ImplSDL3_InitForD3D(window); + ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); + + // Load Fonts + // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. + // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). + // - 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 \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + // [If using SDL_MAIN_USE_CALLBACKS: call ImGui_ImplSDL3_ProcessEvent() from your SDL_AppEvent() function] + SDL_Event event; + while (SDL_PollEvent(&event)) + { + ImGui_ImplSDL3_ProcessEvent(&event); + if (event.type == SDL_EVENT_QUIT) + done = true; + if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)) + done = true; + if (event.type == SDL_EVENT_WINDOW_RESIZED && event.window.windowID == SDL_GetWindowID(window)) + { + // Release all outstanding references to the swap chain's buffers before resizing. + CleanupRenderTarget(); + g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0); + CreateRenderTarget(); + } + } + + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppIterate() function] + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + SDL_Delay(10); + continue; + } + + // Start the Dear ImGui frame + ImGui_ImplDX11_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; + g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr); + g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + + g_pSwapChain->Present(1, 0); // Present with vsync + //g_pSwapChain->Present(0, 0); // Present without vsync + } + + // Cleanup + // [If using SDL_MAIN_USE_CALLBACKS: all code below would likely be your SDL_AppQuit() function] + ImGui_ImplDX11_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + + CleanupDeviceD3D(); + SDL_DestroyWindow(window); + SDL_Quit(); + + return 0; +} + +// Helper functions to use DirectX11 +bool CreateDeviceD3D(HWND hWnd) +{ + // Setup swap chain + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferCount = 2; + sd.BufferDesc.Width = 0; + sd.BufferDesc.Height = 0; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.BufferDesc.RefreshRate.Numerator = 60; + sd.BufferDesc.RefreshRate.Denominator = 1; + sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.OutputWindow = hWnd; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + + UINT createDeviceFlags = 0; + //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; + D3D_FEATURE_LEVEL featureLevel; + const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; + HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available. + res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext); + if (res != S_OK) + return false; + + CreateRenderTarget(); + return true; +} + +void CleanupDeviceD3D() +{ + CleanupRenderTarget(); + if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; } + if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; } + if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; } +} + +void CreateRenderTarget() +{ + ID3D11Texture2D* pBackBuffer; + g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView); + pBackBuffer->Release(); +} + +void CleanupRenderTarget() +{ + if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; } +} diff --git a/examples/example_win32_directx10/main.cpp b/examples/example_win32_directx10/main.cpp index d902dc794..f94440221 100644 --- a/examples/example_win32_directx10/main.cpp +++ b/examples/example_win32_directx10/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 10 +// Dear ImGui: standalone example application for Windows API + DirectX 10 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/examples/example_win32_directx11/main.cpp b/examples/example_win32_directx11/main.cpp index fff7fb2a3..e1ec03664 100644 --- a/examples/example_win32_directx11/main.cpp +++ b/examples/example_win32_directx11/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 11 +// Dear ImGui: standalone example application for Windows API + DirectX 11 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 60b952143..0cd96e2df 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 12 +// Dear ImGui: standalone example application for Windows API + DirectX 12 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index a557823f9..e1cd0fee6 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for DirectX 9 +// Dear ImGui: standalone example application for Windows API + DirectX 9 // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/examples/example_win32_opengl3/main.cpp b/examples/example_win32_opengl3/main.cpp index 7534ce0a7..97b876ac0 100644 --- a/examples/example_win32_opengl3/main.cpp +++ b/examples/example_win32_opengl3/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for Win32 + OpenGL 3 +// Dear ImGui: standalone example application for Windows API + OpenGL // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/examples/example_win32_vulkan/main.cpp b/examples/example_win32_vulkan/main.cpp index 5d040f726..960a96fff 100644 --- a/examples/example_win32_vulkan/main.cpp +++ b/examples/example_win32_vulkan/main.cpp @@ -1,4 +1,4 @@ -// Dear ImGui: standalone example application for Win32 + Vulkan +// Dear ImGui: standalone example application for Windows API + Vulkan // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq diff --git a/examples/imgui_examples.sln b/examples/imgui_examples.sln index cf1c5ad50..8ad1a2566 100644 --- a/examples/imgui_examples.sln +++ b/examples/imgui_examples.sln @@ -39,6 +39,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_win32_vulkan", "exa EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_sdlgpu3", "example_sdl3_sdlgpu3\example_sdl3_sdlgpu3.vcxproj", "{C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example_sdl3_directx11", "example_sdl3_directx11\example_sdl3_directx11.vcxproj", "{009DAC16-1A9C-47BE-9770-A30A046E8090}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -191,6 +193,14 @@ Global {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|Win32.Build.0 = Release|Win32 {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|x64.ActiveCfg = Release|x64 {C22CB6F8-39A5-4DDA-90ED-4ACA4E81E1E5}.Release|x64.Build.0 = Release|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|Win32.ActiveCfg = Debug|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|Win32.Build.0 = Debug|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|x64.ActiveCfg = Debug|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Debug|x64.Build.0 = Debug|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|Win32.ActiveCfg = Release|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|Win32.Build.0 = Release|Win32 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|x64.ActiveCfg = Release|x64 + {009DAC16-1A9C-47BE-9770-A30A046E8090}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/imgui.cpp b/imgui.cpp index a011a07af..65a9385cf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3888,7 +3888,7 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons // Another overly complex function until we reorganize everything into a nice all-in-one helper. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it. // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. -// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceeding 'float ellipsis_max' and was the same value for 99% of users. +// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users. void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) { ImGuiContext& g = *GImGui; @@ -6958,8 +6958,20 @@ static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) return ImGuiCol_WindowBg; } -static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) +static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target_arg, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) { + ImVec2 corner_target = corner_target_arg; + if (window->Flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent + { + ImGuiWindow* parent_window = window->ParentWindow; + ImGuiWindowFlags parent_flags = parent_window->Flags; + ImRect limit_rect = parent_window->InnerRect; + limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize))); + if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar)) + corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x); + if (parent_flags & ImGuiWindowFlags_NoScrollbar) + corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y); + } ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right ImVec2 size_expected = pos_max - pos_min; @@ -7114,7 +7126,8 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si } // Only lower-left grip is visible before hovering/activating - if (resize_grip_n == 0 || held || hovered) + const bool resize_grip_visible = held || hovered || (resize_grip_n == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0); + if (resize_grip_visible) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } @@ -7189,17 +7202,6 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX); ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX); border_target = ImClamp(border_target, clamp_min, clamp_max); - if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent - { - ImGuiWindow* parent_window = window->ParentWindow; - ImGuiWindowFlags parent_flags = parent_window->Flags; - ImRect border_limit_rect = parent_window->InnerRect; - border_limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize))); - if ((axis == ImGuiAxis_X) && ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))) - border_target.x = ImClamp(border_target.x, border_limit_rect.Min.x, border_limit_rect.Max.x); - if ((axis == ImGuiAxis_Y) && (parent_flags & ImGuiWindowFlags_NoScrollbar)) - border_target.y = ImClamp(border_target.y, border_limit_rect.Min.y, border_limit_rect.Max.y); - } if (!ignore_resize) CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); } @@ -8145,9 +8147,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) handle_borders_and_resize_grips = false; // Handle manual resize: Resize Grips, Borders, Gamepad + // Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set. int border_hovered = -1, border_held = -1; ImU32 resize_grip_col[4] = {}; - const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. + int resize_grip_count; + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) + resize_grip_count = ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) ? 1 : 0; + else + resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. + const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); if (handle_borders_and_resize_grips && !window->Collapsed) if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) diff --git a/imgui.h b/imgui.h index bf249e586..00dbeacde 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 19231 +#define IMGUI_VERSION_NUM 19232 #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. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 4f9b0f3e7..717c89f62 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -5604,7 +5604,7 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im } // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. -// DO NOT CALL DIRECTLY THIS WILL CHANGE WIDLY IN 2025-2025. Use ImDrawList::AddText(). +// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2025-2025. Use ImDrawList::AddText(). void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags) { // Align to be pixel perfect diff --git a/misc/README.txt b/misc/README.txt index b4ce89f03..58c00ee8c 100644 --- a/misc/README.txt +++ b/misc/README.txt @@ -4,8 +4,8 @@ misc/cpp/ This is also an example of how you may wrap your own similar types. misc/debuggers/ - Helper files for popular debuggers. - With the .natvis file, types like ImVector<> will be displayed nicely in Visual Studio debugger. + Helper files for popular debuggers (Visual Studio, GDB, LLDB). + e.g. With the .natvis file, types like ImVector<> will be displayed nicely in Visual Studio debugger. misc/fonts/ Fonts loading/merging instructions (e.g. How to handle glyph ranges, how to merge icons fonts). diff --git a/misc/debuggers/README.txt b/misc/debuggers/README.txt index 3f4ba83e3..ad6f491fe 100644 --- a/misc/debuggers/README.txt +++ b/misc/debuggers/README.txt @@ -14,3 +14,8 @@ imgui.natvis With this, types like ImVector<> will be displayed nicely in the debugger. (read comments inside file for details) +imgui_lldb.py + LLDB-based debuggers (*): synthetic children provider and summaries for Dear ImGui types. + With this, types like ImVector<> will be displayed nicely in the debugger. + (read comments inside file for details) + (*) Xcode, Android Studio, may be used from VS Code, C++Builder, CLion, Eclipse etc. diff --git a/misc/debuggers/imgui_lldb.py b/misc/debuggers/imgui_lldb.py new file mode 100644 index 000000000..7d3c6bfcb --- /dev/null +++ b/misc/debuggers/imgui_lldb.py @@ -0,0 +1,189 @@ +# This file implements synthetic children providers and summaries for various Dear ImGui types for LLDB. +# LLDB is used by Xcode, Android Studio, and may be used from VS Code, C++Builder, CLion, Eclipse etc. + +# +# Useful links/documentation related to the feature: +# - https://lldb.llvm.org/use/variable.html#summary-strings +# - https://lldb.llvm.org/use/variable.html#synthetic-children +# - https://lldb.llvm.org/python_reference/lldb-module.html +# +# To use it in a debug session: +# > (lldb) command script import +# +# Alternatively you may include the above command in your ~/.lldbinit file to have the formatters +# available in all future sessions + +import lldb + +class ArraySynthBase(object): + """ + Helper baseclass aimed to reduce the boilerplate needed for "array-like" containers + """ + + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def bind_to(self, pointer, size): + array_p = pointer.GetType().GetPointeeType().GetArrayType(size).GetPointerType() + self.array = pointer.Cast(array_p).Dereference() + + def update(self): + self.array = self.valobj + + def num_children(self, max_children): + return self.array.GetNumChildren(max_children) + + def get_child_index(self, name): + return self.array.GetIndexOfChildWithName(name) + + def get_child_at_index(self, index): + return self.array.GetChildAtIndex(index) + + def has_children(self): + return self.array.MightHaveChildren() + + def get_value(self): + return self.array + +class ImVectorSynth(ArraySynthBase): + def update(self): + self.size = self.valobj.GetChildMemberWithName("Size").GetValueAsUnsigned() + self.capacity = self.valobj.GetChildMemberWithName("Capacity").GetValueAsUnsigned() + + data = self.valobj.GetChildMemberWithName("Data") + + self.bind_to(data, self.size) + + def get_summary(self): + return f"Size={self.size} Capacity={self.capacity}" + +class ImSpanSynth(ArraySynthBase): + def update(self): + data = self.valobj.GetChildMemberWithName("Data") + end = self.valobj.GetChildMemberWithName("DataEnd") + + element_size = data.GetType().GetPointeeType().GetByteSize() + array_size = end.GetValueAsUnsigned() - data.GetValueAsUnsigned() + + self.size = int(array_size / element_size) + + self.bind_to(data, self.size) + + def get_summary(self): + return f"Size={self.size}" + +class ImRectSummary(object): + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def update(self): + pass + + def get_summary(self): + min = self.valobj.GetChildMemberWithName("Min") + max = self.valobj.GetChildMemberWithName("Max") + + minX = float(min.GetChildMemberWithName("x").GetValue()) + minY = float(min.GetChildMemberWithName("y").GetValue()) + + maxX = float(max.GetChildMemberWithName("x").GetValue()) + maxY = float(max.GetChildMemberWithName("y").GetValue()) + + return f"Min=({minX}, {minY}) Max=({maxX}, {maxY}) Size=({maxX - minX}, {maxY - minY})" + +def get_active_enum_flags(valobj): + flag_set = set() + + enum_name = valobj.GetType().GetName() + "_" + enum_type = valobj.GetTarget().FindFirstType(enum_name) + + if not enum_type.IsValid(): + return flag_set + + enum_members = enum_type.GetEnumMembers() + value = valobj.GetValueAsUnsigned() + + for i in range(0, enum_members.GetSize()): + member = enum_members.GetTypeEnumMemberAtIndex(i) + + if value & member.GetValueAsUnsigned(): + flag_set.add(member.GetName().removeprefix(enum_name)) + + return flag_set + +class ImGuiWindowSummary(object): + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def update(self): + pass + + def get_summary(self): + name = self.valobj.GetChildMemberWithName("Name").GetSummary() + + active = self.valobj.GetChildMemberWithName("Active").GetValueAsUnsigned() != 0 + was_active = self.valobj.GetChildMemberWithName("WasActive").GetValueAsUnsigned() != 0 + hidden = self.valobj.GetChildMemberWithName("Hidden") != 0 + + flags = get_active_enum_flags(self.valobj.GetChildMemberWithName("Flags")) + + active = 1 if active or was_active else 0 + child = 1 if "ChildWindow" in flags else 0 + popup = 1 if "Popup" in flags else 0 + hidden = 1 if hidden else 0 + + return f"Name {name} Active {active} Child {child} Popup {popup} Hidden {hidden}" + + +def __lldb_init_module(debugger, internal_dict): + """ + This function will be automatically called by LLDB when the module is loaded, here + we register the various synthetics/summaries we have build before + """ + + category_name = "imgui" + category = debugger.GetCategory(category_name) + + # Make sure we don't accidentally keep accumulating languages or override the user's + # category enablement in Xcode, where lldb-rpc-server loads this file once for eac + # debugging session + if not category.IsValid(): + category = debugger.CreateCategory(category_name) + category.AddLanguage(lldb.eLanguageTypeC_plus_plus) + category.SetEnabled(True) + + def add_summary(typename, impl): + summary = None + + if isinstance(impl, str): + summary = lldb.SBTypeSummary.CreateWithSummaryString(impl) + summary.SetOptions(lldb.eTypeOptionCascade) + else: + # Unfortunately programmatic summary string generation is an entirely different codepath + # in LLDB. Register a convenient trampoline function which makes it look like it's part + # of the SyntheticChildrenProvider contract + summary = lldb.SBTypeSummary.CreateWithScriptCode(f''' + synth = {impl.__module__}.{impl.__qualname__}(valobj.GetNonSyntheticValue(), internal_dict) + synth.update() + + return synth.get_summary() + ''') + summary.SetOptions(lldb.eTypeOptionCascade | lldb.eTypeOptionFrontEndWantsDereference) + + category.AddTypeSummary(lldb.SBTypeNameSpecifier(typename, True), summary) + + def add_synthetic(typename, impl): + add_summary(typename, impl) + + synthetic = lldb.SBTypeSynthetic.CreateWithClassName(f"{impl.__module__}.{impl.__qualname__}") + synthetic.SetOptions(lldb.eTypeOptionCascade | lldb.eTypeOptionFrontEndWantsDereference) + + category.AddTypeSynthetic(lldb.SBTypeNameSpecifier(typename, True), synthetic) + + add_synthetic("^ImVector<.+>$", ImVectorSynth) + add_synthetic("^ImSpan<.+>$", ImSpanSynth) + + add_summary("^ImVec2$", "x=${var.x} y=${var.y}") + add_summary("^ImVec4$", "x=${var.x} y=${var.y} z=${var.z} w=${var.w}") + add_summary("^ImRect$", ImRectSummary) + add_summary("^ImGuiWindow$", ImGuiWindowSummary)