From adbbd17cb66f5a1635d209b55d8599fcad369e5f Mon Sep 17 00:00:00 2001 From: omar Date: Tue, 18 Jun 2019 23:35:48 +0200 Subject: [PATCH] Addendum to #2635. Add support for multi-viewports in SDL+DX!! example. making all Win32-centric back-ends handle PlatformHandleRaw. Using the field to use/store the HWND for internal purpose in SDL/GLFW back-ends. (#1542) --- examples/example_sdl_directx11/main.cpp | 21 ++++++++++- examples/imgui_impl_dx10.cpp | 4 ++- examples/imgui_impl_dx11.cpp | 10 ++---- examples/imgui_impl_dx12.cpp | 7 ++-- examples/imgui_impl_dx12.h | 3 +- examples/imgui_impl_dx9.cpp | 8 +++-- examples/imgui_impl_glfw.cpp | 23 ++++++++----- examples/imgui_impl_sdl.cpp | 46 ++++++++++++------------- examples/imgui_impl_vulkan.cpp | 2 +- examples/imgui_impl_win32.cpp | 4 +-- imgui.h | 4 +-- 11 files changed, 79 insertions(+), 53 deletions(-) diff --git a/examples/example_sdl_directx11/main.cpp b/examples/example_sdl_directx11/main.cpp index ae523fe9..3c7ee18e 100644 --- a/examples/example_sdl_directx11/main.cpp +++ b/examples/example_sdl_directx11/main.cpp @@ -51,13 +51,25 @@ int main(int, char**) IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer bindings ImGui_ImplSDL2_InitForD3D(window); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); @@ -149,6 +161,13 @@ int main(int, char**) g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, (float*)&clear_color); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync } diff --git a/examples/imgui_impl_dx10.cpp b/examples/imgui_impl_dx10.cpp index c5fdaeb4..cf1c1c44 100644 --- a/examples/imgui_impl_dx10.cpp +++ b/examples/imgui_impl_dx10.cpp @@ -553,7 +553,9 @@ static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx10* data = IM_NEW(ImGuiViewportDataDx10)(); viewport->RendererUserData = data; - HWND hwnd = (HWND)viewport->PlatformHandle; + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_dx11.cpp b/examples/imgui_impl_dx11.cpp index b0aad0e1..02236296 100644 --- a/examples/imgui_impl_dx11.cpp +++ b/examples/imgui_impl_dx11.cpp @@ -564,13 +564,9 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx11* data = IM_NEW(ImGuiViewportDataDx11)(); viewport->RendererUserData = data; - // When using SDL, PlatformHandleRaw will be the HWND (because PlatformHandle would be the SDL_Window) - // If not using SDL, PlatformHandleRaw will be null and PlatformHandle will contain the HWND - HWND hwnd = (HWND)viewport->PlatformHandleRaw; - if (hwnd == 0) - { - hwnd = (HWND)viewport->PlatformHandle; - } + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-end will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_dx12.cpp b/examples/imgui_impl_dx12.cpp index 7dfdb9c6..04b27113 100644 --- a/examples/imgui_impl_dx12.cpp +++ b/examples/imgui_impl_dx12.cpp @@ -4,7 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// Issues: +// Missing features, issues: +// [ ] Renderer: Missing multi-viewport support. // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. @@ -680,7 +681,9 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) /* // FIXME-PLATFORM - HWND hwnd = (HWND)viewport->PlatformHandle; + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); // Create swap chain diff --git a/examples/imgui_impl_dx12.h b/examples/imgui_impl_dx12.h index 8ae75e53..cf34c144 100644 --- a/examples/imgui_impl_dx12.h +++ b/examples/imgui_impl_dx12.h @@ -4,7 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. -// Issues: +// Missing features, issues: +// [ ] Renderer: Missing multi-viewport support. // [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index a8635fe0..2c22885e 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -319,8 +319,10 @@ static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) ImGuiViewportDataDx9* data = IM_NEW(ImGuiViewportDataDx9)(); viewport->RendererUserData = data; - HWND hWnd = (HWND)viewport->PlatformHandle; - IM_ASSERT(hWnd != 0); + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); data->d3dpp.Windowed = TRUE; @@ -328,7 +330,7 @@ static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) data->d3dpp.BackBufferWidth = (UINT)viewport->Size.x; data->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; - data->d3dpp.hDeviceWindow = hWnd; + data->d3dpp.hDeviceWindow = hwnd; data->d3dpp.EnableAutoDepthStencil = FALSE; data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 21da2f69..c4b60217 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -202,6 +202,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)g_Window; +#ifdef _WIN32 + main_viewport->PlatformHandleRaw = glfwGetWin32Window(g_Window); +#endif if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplGlfw_InitPlatformInterface(); @@ -444,6 +447,9 @@ static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) data->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", NULL, share_window); data->WindowOwned = true; viewport->PlatformHandle = (void*)data->Window; +#ifdef _WIN32 + viewport->PlatformHandleRaw = glfwGetWin32Window(data->Window); +#endif glfwSetWindowPos(data->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); // Install callbacks for secondary viewports @@ -468,7 +474,7 @@ static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) if (data->WindowOwned) { #if GLFW_HAS_GLFW_HOVERED && defined(_WIN32) - HWND hwnd = glfwGetWin32Window(data->Window); + HWND hwnd = (HWND)viewport->PlatformHandleRaw; ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); #endif glfwDestroyWindow(data->Window); @@ -504,7 +510,7 @@ static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) #if defined(_WIN32) // GLFW hack: Hide icon from task bar - HWND hwnd = glfwGetWin32Window(data->Window); + HWND hwnd = (HWND)viewport->PlatformHandleRaw; if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); @@ -633,13 +639,12 @@ static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) static void ImGui_ImplWin32_SetImeInputPos(ImGuiViewport* viewport, ImVec2 pos) { COMPOSITIONFORM cf = { CFS_FORCE_POSITION, { (LONG)(pos.x - viewport->Pos.x), (LONG)(pos.y - viewport->Pos.y) }, { 0, 0, 0, 0 } }; - if (ImGuiViewportDataGlfw* data = (ImGuiViewportDataGlfw*)viewport->PlatformUserData) - if (HWND hwnd = glfwGetWin32Window(data->Window)) - if (HIMC himc = ::ImmGetContext(hwnd)) - { - ::ImmSetCompositionWindow(himc, &cf); - ::ImmReleaseContext(hwnd, himc); - } + if (HWND hwnd = (HWND)viewport->PlatformHandleRaw) + if (HIMC himc = ::ImmGetContext(hwnd)) + { + ::ImmSetCompositionWindow(himc, &cf); + ::ImmReleaseContext(hwnd, himc); + } } #else #define HAS_WIN32_IME 0 diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index e115e27f..3ee2b82e 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -200,6 +200,12 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)window; +#if defined(_WIN32) + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(window, &info)) + main_viewport->PlatformHandleRaw = info.info.win.window; +#endif // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. @@ -455,16 +461,13 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) } if (use_opengl && backup_context) SDL_GL_MakeCurrent(data->Window, backup_context); - viewport->PlatformHandle = (void*)data->Window; + viewport->PlatformHandle = (void*)data->Window; #if defined(_WIN32) - // save the window handle for render that needs it (directX) SDL_SysWMinfo info; SDL_VERSION(&info.version); if (SDL_GetWindowWMInfo(data->Window, &info)) - { viewport->PlatformHandleRaw = info.info.win.window; - } #endif } @@ -487,28 +490,23 @@ static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) { ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; #if defined(_WIN32) - SDL_SysWMinfo info; - SDL_VERSION(&info.version); - if (SDL_GetWindowWMInfo(data->Window, &info)) + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + + // SDL hack: Hide icon from task bar + // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) { - HWND hwnd = info.info.win.window; + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } - // SDL hack: Hide icon from task bar - // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. - if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) - { - LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); - ex_style &= ~WS_EX_APPWINDOW; - ex_style |= WS_EX_TOOLWINDOW; - ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); - } - - // SDL hack: SDL always activate/focus windows :/ - if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) - { - ::ShowWindow(hwnd, SW_SHOWNA); - return; - } + // SDL hack: SDL always activate/focus windows :/ + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; } #endif diff --git a/examples/imgui_impl_vulkan.cpp b/examples/imgui_impl_vulkan.cpp index 16d2f4b6..74d111ea 100644 --- a/examples/imgui_impl_vulkan.cpp +++ b/examples/imgui_impl_vulkan.cpp @@ -3,8 +3,8 @@ // Implemented features: // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices. +// [x] Platform: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // Missing features: -// [ ] Platform: Multi-viewport / platform windows. // [ ] Renderer: User texture binding. Changes of ImTextureID aren't supported by this binding! See https://github.com/ocornut/imgui/pull/914 // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index e1ae9730..d1a42a2f 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -73,7 +73,7 @@ bool ImGui_ImplWin32_Init(void* hwnd) // Our mouse update function expect PlatformHandle to be filled for the main viewport g_hWnd = (HWND)hwnd; ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - main_viewport->PlatformHandle = (void*)g_hWnd; + main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)g_hWnd; if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplWin32_InitPlatformInterface(); @@ -547,7 +547,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param data->HwndOwned = true; viewport->PlatformRequestResize = false; - viewport->PlatformHandle = data->Hwnd; + viewport->PlatformHandle = viewport->PlatformHandleRaw = data->Hwnd; } static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) diff --git a/imgui.h b/imgui.h index be07f2fe..b4539b46 100644 --- a/imgui.h +++ b/imgui.h @@ -2398,8 +2398,8 @@ struct ImGuiViewport void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, frame-buffers etc.) void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context) - void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GlfwWindow*, SDL_Window*) - void* PlatformHandleRaw; // void* to hold the platfor-native windows handle (e.g. the HWND) when using an abstraction layer like SDL (where PlatformHandle would be a SDL_Window*) + void* PlatformHandle; // void* for FindViewportByPlatformHandle(). (e.g. suggested to use natural platform handle such as HWND, GLFWWindow*, SDL_Window*) + void* PlatformHandleRaw; // void* to hold low-level, platform-native window handle (e.g. the HWND) when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size)