From b9db5c566bbafa3462f1a526032ca2971742db17 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 15 Jun 2023 11:26:27 +0200 Subject: [PATCH] Backends: Win32: Added support for updating ParentViewportID after viewport creation. This tends to be desirable for applicaton manipulating this value via ImGuiWindowClass::ParentViewportID. I thought it would be a simple call to SetParent(), it's only slightly more complicated but added comments about it. --- backends/imgui_impl_win32.cpp | 38 +++++++++++++++++++++++++++-------- docs/CHANGELOG.txt | 1 + 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index d24c655d8..f6d735cdd 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -950,11 +950,12 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) struct ImGui_ImplWin32_ViewportData { HWND Hwnd; + HWND HwndParent; bool HwndOwned; DWORD DwStyle; DWORD DwExStyle; - ImGui_ImplWin32_ViewportData() { Hwnd = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; } + ImGui_ImplWin32_ViewportData() { Hwnd = HwndParent = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; } ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == nullptr); } }; @@ -974,6 +975,14 @@ static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags fl *out_ex_style |= WS_EX_TOPMOST; } +static HWND ImGui_ImplWin32_GetHwndFromViewportID(ImGuiID viewport_id) +{ + if (viewport_id != 0) + if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id)) + return (HWND)viewport->PlatformHandle; + return nullptr; +} + static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) { ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); @@ -981,10 +990,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) // Select style and parent window ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle); - HWND parent_window = nullptr; - if (viewport->ParentViewportId != 0) - if (ImGuiViewport* parent_viewport = ImGui::FindViewportByID(viewport->ParentViewportId)) - parent_window = (HWND)parent_viewport->PlatformHandle; + vd->HwndParent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId); // Create window RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; @@ -992,7 +998,7 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) vd->Hwnd = ::CreateWindowEx( vd->DwExStyle, _T("ImGui Platform"), _T("Untitled"), vd->DwStyle, // Style, class name, window name rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area - parent_window, nullptr, ::GetModuleHandle(nullptr), nullptr); // Parent window, Menu, Instance, Param + vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr); // Owner window, Menu, Instance, Param vd->HwndOwned = true; viewport->PlatformRequestResize = false; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; @@ -1029,10 +1035,26 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) { - // (Optional) Update Win32 style if it changed _after_ creation. - // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; IM_ASSERT(vd->Hwnd != 0); + + // Update Win32 parent if it changed _after_ creation + // Unlike style settings derived from configuration flags, this is more likely to change for advanced apps that are manipulating ParentViewportID manually. + HWND new_parent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId); + if (new_parent != vd->HwndParent) + { + // Win32 windows can either have a "Parent" (for WS_CHILD window) or an "Owner" (which among other thing keeps window above its owner). + // Our Dear Imgui-side concept of parenting only mostly care about what Win32 call "Owner". + // The parent parameter of CreateWindowEx() sets up Parent OR Owner depending on WS_CHILD flag. In our case an Owner as we never use WS_CHILD. + // Calling ::SetParent() here would be incorrect: it will create a full child relation, alter coordinate system and clipping. + // Calling ::SetWindowLongPtr() with GWLP_HWNDPARENT seems correct although poorly documented. + // https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613 + vd->HwndParent = new_parent; + ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent); + } + + // (Optional) Update Win32 style if it changed _after_ creation. + // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. DWORD new_style; DWORD new_ex_style; ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f45c78d8a..b20d9c7c5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -114,6 +114,7 @@ Docking+Viewports Branch: keeping ID alive when calling low-level ButtonBehavior() directly). (#5181, #2645) - Docking: [Internal] DockBuilderDockWindow() API calls don't clear docking order if the target node is same as existing one. +- Backends: Win32: Added support for changing ParentViewportID after viewport creation. -----------------------------------------------------------------------