diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index b5347cda0..c3901ab1c 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -31,6 +31,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-08-12: [Docking] Multi-viewport: Synchronize window manager adjustments instead of treating them as setter echoes. On X11, poll live geometry after a setter so fully rejected requests still synchronize. (#9356, #9398, #9442) +// 2026-08-12: [Docking] Multi-viewport: Disable support on Wayland with legacy GLFW versions as global window positioning is unavailable. // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2026-04-21: Added a Win32-specific implementation of ImGui_ImplGlfw_GetContentScaleXXXX functions for legacy GLFW 3.2. // 2026-03-25: Mouse cursor is properly restored if changed by user app/code while using glfwSetInputMode(..., GLFW_CURSOR_DISABLED) or ImGuiConfigFlags_NoMouseCursorChange. Amend change from 2025-12-10. @@ -310,6 +312,9 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window) static void ImGui_ImplGlfw_UpdateMonitors(); static void ImGui_ImplGlfw_InitMultiViewportSupport(); static void ImGui_ImplGlfw_ShutdownMultiViewportSupport(); +#if GLFW_HAS_X11 +static void ImGui_ImplGlfw_UpdateWindowGeometryRequests(); +#endif // Functions static bool ImGui_ImplGlfw_IsWayland() @@ -323,10 +328,10 @@ static bool ImGui_ImplGlfw_IsWayland() if (strstr(version, "Wayland") == nullptr) // e.g. Ubuntu 22.04 ships with GLFW 3.3.6 compiled without Wayland return false; #ifdef GLFW_EXPOSE_NATIVE_X11 - if (glfwGetX11Display() != nullptr) - return false; + return glfwGetX11Display() == nullptr; +#else + return false; // Missing X11 native API: do not assume Wayland (keeps viewports on X11). #endif - return true; #endif } @@ -739,11 +744,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bool has_viewports = false; #ifndef __EMSCRIPTEN__ - has_viewports = true; -#if GLFW_HAS_GETPLATFORM - if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) - has_viewports = false; -#endif + has_viewports = !bd->IsWayland; if (has_viewports) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #endif @@ -835,11 +836,14 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw const char* x11_module_path = "libX11.so.6"; #endif bd->X11Module = dlopen(x11_module_path, RTLD_LAZY | RTLD_LOCAL); - bd->XInternAtom = (PFN_XInternAtom)dlsym(bd->X11Module, "XInternAtom"); - bd->XChangeProperty = (PFN_XChangeProperty)dlsym(bd->X11Module, "XChangeProperty"); - bd->XChangeWindowAttributes = (PFN_XChangeWindowAttributes)dlsym(bd->X11Module, "XChangeWindowAttributes"); - bd->XFlush = (PFN_XFlush)dlsym(bd->X11Module, "XFlush"); - IM_ASSERT(bd->XInternAtom != nullptr && bd->XChangeProperty != nullptr && bd->XChangeWindowAttributes != nullptr && bd->XFlush != nullptr); + if (bd->X11Module != nullptr) + { + bd->XInternAtom = (PFN_XInternAtom)dlsym(bd->X11Module, "XInternAtom"); + bd->XChangeProperty = (PFN_XChangeProperty)dlsym(bd->X11Module, "XChangeProperty"); + bd->XChangeWindowAttributes = (PFN_XChangeWindowAttributes)dlsym(bd->X11Module, "XChangeWindowAttributes"); + bd->XFlush = (PFN_XFlush)dlsym(bd->X11Module, "XFlush"); + IM_ASSERT(bd->XInternAtom != nullptr && bd->XChangeProperty != nullptr && bd->XChangeWindowAttributes != nullptr && bd->XFlush != nullptr); + } } #endif @@ -1201,6 +1205,9 @@ void ImGui_ImplGlfw_NewFrame() // Setup main viewport size (every frame to accommodate for window resizing) ImGui_ImplGlfw_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale); ImGui_ImplGlfw_UpdateMonitors(); +#if GLFW_HAS_X11 + ImGui_ImplGlfw_UpdateWindowGeometryRequests(); +#endif // Setup time step // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) @@ -1294,6 +1301,18 @@ struct ImGui_ImplGlfw_ViewportData bool WindowOwned; int IgnoreWindowPosEventFrame; int IgnoreWindowSizeEventFrame; + int LastWindowPosRequest[2]; + int LastWindowSizeRequest[2]; +#if GLFW_HAS_X11 + int LastObservedWindowPos[2]; + int LastObservedWindowSize[2]; + int WindowPosMismatchFrameCount; + int WindowSizeMismatchFrameCount; + double WindowPosMismatchStartTime; + double WindowSizeMismatchStartTime; + bool WindowPosRequestPending; + bool WindowSizeRequestPending; +#endif #ifdef _WIN32 WNDPROC PrevWndProc; #endif @@ -1314,30 +1333,100 @@ static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) // - on Linux it is queued and invoked during glfwPollEvents() // Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only // ignore recent glfwSetWindowXXX() calls. -static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) +static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int x, int y) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1); - //data->IgnoreWindowPosEventFrame = -1; - if (ignore_event) + // Ignore only the latest matching setter echo. Adjusted or stale geometry requests a platform readback. + const bool is_latest_request = (x == vd->LastWindowPosRequest[0] && y == vd->LastWindowPosRequest[1]); + const bool is_current_viewport_pos = (x == (int)viewport->Pos.x && y == (int)viewport->Pos.y); +#if GLFW_HAS_X11 + vd->WindowPosRequestPending = false; + vd->WindowPosMismatchFrameCount = 0; + vd->WindowPosMismatchStartTime = 0.0; +#endif + if (ignore_event && is_latest_request && is_current_viewport_pos) return; } viewport->PlatformRequestMove = true; } } -static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) +#if GLFW_HAS_X11 +static bool ImGui_ImplGlfw_UpdateWindowGeometryRequest(int value_0, int value_1, const int request[2], int observed[2], int* mismatch_frame_count, double* mismatch_start_time, bool* request_pending, double current_time) +{ + if (value_0 == request[0] && value_1 == request[1]) + { + *request_pending = false; + *mismatch_frame_count = 0; + *mismatch_start_time = 0.0; + return false; + } + if (*mismatch_frame_count > 0 && value_0 == observed[0] && value_1 == observed[1]) + (*mismatch_frame_count)++; + else + { + *mismatch_frame_count = 1; + *mismatch_start_time = current_time; + } + observed[0] = value_0; + observed[1] = value_1; + if (*mismatch_frame_count < 2 || current_time - *mismatch_start_time < 0.050) + return false; + *request_pending = false; + *mismatch_frame_count = 0; + *mismatch_start_time = 0.0; + return true; +} + +// GLFW swallows unchanged synthetic ConfigureNotify. Poll live geometry until it matches the last Set or stays rejected. +static void ImGui_ImplGlfw_UpdateWindowGeometryRequests() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + const double current_time = glfwGetTime(); + for (int viewport_n = 1; viewport_n < platform_io.Viewports.Size; viewport_n++) + { + ImGuiViewport* viewport = platform_io.Viewports[viewport_n]; + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + if (vd == nullptr) + continue; + if (vd->WindowPosRequestPending) + { + int x, y; + glfwGetWindowPos(vd->Window, &x, &y); + if (ImGui_ImplGlfw_UpdateWindowGeometryRequest(x, y, vd->LastWindowPosRequest, vd->LastObservedWindowPos, &vd->WindowPosMismatchFrameCount, &vd->WindowPosMismatchStartTime, &vd->WindowPosRequestPending, current_time)) + viewport->PlatformRequestMove = true; + } + if (vd->WindowSizeRequestPending) + { + int width, height; + glfwGetWindowSize(vd->Window, &width, &height); + if (ImGui_ImplGlfw_UpdateWindowGeometryRequest(width, height, vd->LastWindowSizeRequest, vd->LastObservedWindowSize, &vd->WindowSizeMismatchFrameCount, &vd->WindowSizeMismatchStartTime, &vd->WindowSizeRequestPending, current_time)) + viewport->PlatformRequestResize = true; + } + } +} +#endif + +static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int width, int height) { if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) { if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) { bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1); - //data->IgnoreWindowSizeEventFrame = -1; - if (ignore_event) + // Preserve delayed setter-echo suppression only while the viewport still expects that exact size. + const bool is_latest_request = (width == vd->LastWindowSizeRequest[0] && height == vd->LastWindowSizeRequest[1]); + const bool is_current_viewport_size = (width == (int)viewport->Size.x && height == (int)viewport->Size.y); +#if GLFW_HAS_X11 + vd->WindowSizeRequestPending = false; + vd->WindowSizeMismatchFrameCount = 0; + vd->WindowSizeMismatchStartTime = 0.0; +#endif + if (ignore_event && is_latest_request && is_current_viewport_size) return; } viewport->PlatformRequestResize = true; @@ -1349,7 +1438,7 @@ static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) static void ImGui_ImplGlfw_SetWindowFloating(ImGui_ImplGlfw_Data* bd, GLFWwindow* window) { #ifdef GLFW_EXPOSE_NATIVE_X11 - if (glfwGetPlatform() == GLFW_PLATFORM_X11) + if (glfwGetPlatform() == GLFW_PLATFORM_X11 && bd->XInternAtom != nullptr && bd->XChangeProperty != nullptr && bd->XChangeWindowAttributes != nullptr && bd->XFlush != nullptr) { Display* display = glfwGetX11Display(); Window xwindow = glfwGetX11Window(window); @@ -1499,7 +1588,22 @@ static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) { ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); - glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y); +#if GLFW_HAS_X11 + const int request_x = (int)pos.x; + const int request_y = (int)pos.y; + if (request_x != vd->LastWindowPosRequest[0] || request_y != vd->LastWindowPosRequest[1]) + { + vd->WindowPosMismatchFrameCount = 0; + vd->WindowPosMismatchStartTime = 0.0; + } + vd->LastWindowPosRequest[0] = request_x; + vd->LastWindowPosRequest[1] = request_y; + vd->WindowPosRequestPending = true; +#else + vd->LastWindowPosRequest[0] = (int)pos.x; + vd->LastWindowPosRequest[1] = (int)pos.y; +#endif + glfwSetWindowPos(vd->Window, vd->LastWindowPosRequest[0], vd->LastWindowPosRequest[1]); } static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) @@ -1521,10 +1625,28 @@ static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) int x, y, width, height; glfwGetWindowPos(vd->Window, &x, &y); glfwGetWindowSize(vd->Window, &width, &height); - glfwSetWindowPos(vd->Window, x, y - height + size.y); + vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); + vd->LastWindowPosRequest[0] = x; + vd->LastWindowPosRequest[1] = (int)(y - height + size.y); + glfwSetWindowPos(vd->Window, vd->LastWindowPosRequest[0], vd->LastWindowPosRequest[1]); #endif vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); - glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); +#if GLFW_HAS_X11 + const int request_w = (int)size.x; + const int request_h = (int)size.y; + if (request_w != vd->LastWindowSizeRequest[0] || request_h != vd->LastWindowSizeRequest[1]) + { + vd->WindowSizeMismatchFrameCount = 0; + vd->WindowSizeMismatchStartTime = 0.0; + } + vd->LastWindowSizeRequest[0] = request_w; + vd->LastWindowSizeRequest[1] = request_h; + vd->WindowSizeRequestPending = true; +#else + vd->LastWindowSizeRequest[0] = (int)size.x; + vd->LastWindowSizeRequest[1] = (int)size.y; +#endif + glfwSetWindowSize(vd->Window, vd->LastWindowSizeRequest[0], vd->LastWindowSizeRequest[1]); } static ImVec2 ImGui_ImplGlfw_GetWindowFramebufferScale(ImGuiViewport* viewport) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 89f150d16..a27a61e86 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -113,7 +113,15 @@ Other Changes: Docking+Viewports Branch: +- Viewports: fixed platform position/size requests adjusted or rejected by a window manager + leaving Dear ImGui geometry out of sync with the native window. Active window dragging now + continues from the platform-accepted position instead of repeatedly requesting rejected + geometry. (#9356, #9398, #9442) - Backends: + - GLFW: on X11, poll live geometry after a setter so completely rejected viewport + requests are synchronized. (#9356, #9398, #9442) + - GLFW: disabled multi-viewports on Wayland with legacy GLFW versions, where global window + positioning is unavailable. - Metal: fixed resizing windows losing framebuffer scale. (#6828, #8856) [@laikoken] - Examples: - SDL3+Metal, GLFW+Metal: fix multi-viewport support. @@ -9714,4 +9722,3 @@ Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.00 - Initial release. - diff --git a/imgui.cpp b/imgui.cpp index 906285d38..88d79e1cc 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4334,6 +4334,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) HoveredWindowUnderMovingWindow = NULL; HoveredWindowBeforeClear = NULL; MovingWindow = NULL; + MovingWindowClickOffset = ImVec2(-1, -1); WheelingWindow = NULL; WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; WheelingWindowReleaseTimer = 0.0f; @@ -5464,6 +5465,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos; + g.MovingWindowClickOffset = g.ActiveIdClickOffset; g.ActiveIdNoClearOnFocusLoss = true; SetActiveIdUsingAllKeyboardKeys(); @@ -5549,7 +5551,7 @@ void ImGui::UpdateMouseMovingWindowNewFrame() const bool window_disappeared = (!moving_window->WasActive && !moving_window->Active); if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappeared) { - ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; + ImVec2 pos = g.IO.MousePos - g.MovingWindowClickOffset; if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) { SetWindowPos(moving_window, pos, ImGuiCond_Always); @@ -17210,9 +17212,43 @@ static void ImGui::UpdateViewportsNewFrame() { // Viewport->WorkPos and WorkSize will be updated below if (viewport->PlatformRequestMove) - viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); + { + const ImVec2 requested_pos = viewport->LastPlatformPos; + const ImVec2 platform_pos = g.PlatformIO.Platform_GetWindowPos(viewport); + // Rebase only axes adjusted by the window manager. Platform coordinates are integer pixels. + const bool platform_pos_adjusted = (ImTrunc(platform_pos.x) != ImTrunc(requested_pos.x) || ImTrunc(platform_pos.y) != ImTrunc(requested_pos.y)); + ImGuiWindow* moving_root = g.MovingWindow ? g.MovingWindow->RootWindowDockTree : NULL; + const bool moving_owned_viewport = (moving_root && viewport->Window == moving_root && moving_root->ViewportOwned && IsMousePosValid(&g.IO.MousePos)); + if (moving_owned_viewport) + { + if (ImTrunc(platform_pos.x) != ImTrunc(requested_pos.x)) + g.MovingWindowClickOffset.x = g.IO.MousePos.x - platform_pos.x; + if (ImTrunc(platform_pos.y) != ImTrunc(requested_pos.y)) + g.MovingWindowClickOffset.y = g.IO.MousePos.y - platform_pos.y; + } + viewport->Pos = viewport->LastPlatformPos = platform_pos; + if (platform_pos_adjusted && viewport->Window != NULL) + { + viewport->Window->Pos = platform_pos; + MarkIniSettingsDirty(viewport->Window); + } + if (!platform_pos_adjusted || moving_owned_viewport) + viewport->PlatformRequestMove = false; + } if (viewport->PlatformRequestResize) - viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); + { + const ImVec2 requested_size = viewport->LastPlatformSize; + const ImVec2 platform_size = g.PlatformIO.Platform_GetWindowSize(viewport); + const bool platform_size_adjusted = (ImTrunc(platform_size.x) != ImTrunc(requested_size.x) || ImTrunc(platform_size.y) != ImTrunc(requested_size.y)); + viewport->Size = viewport->LastPlatformSize = platform_size; + if (platform_size_adjusted && viewport->Window != NULL) + { + viewport->Window->Size = viewport->Window->SizeFull = platform_size; + MarkIniSettingsDirty(viewport->Window); + } + if (!platform_size_adjusted) + viewport->PlatformRequestResize = false; + } if (g.PlatformIO.Platform_GetWindowFramebufferScale != NULL) viewport->FramebufferScale = g.PlatformIO.Platform_GetWindowFramebufferScale(viewport); } @@ -17763,15 +17799,25 @@ void ImGui::UpdatePlatformWindows() viewport->PlatformWindowCreated = true; } + // Consume requests applied by NewFrame(), before setters can raise synchronous callbacks. + const bool platform_request_move = viewport->PlatformRequestMove; + const bool platform_request_resize = viewport->PlatformRequestResize; + viewport->PlatformRequestMove = viewport->PlatformRequestResize = false; + // Apply Position and Size (from ImGui to Platform/Renderer backends) - if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) + if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !platform_request_move) + { g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); - if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) + viewport->LastPlatformPos = viewport->Pos; + } + if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !platform_request_resize) + { g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); + viewport->LastPlatformSize = viewport->Size; + } if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); - viewport->LastPlatformPos = viewport->Pos; - viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; + viewport->LastRendererSize = viewport->Size; // Update title bar (if it changed) if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window)) @@ -17808,8 +17854,10 @@ void ImGui::UpdatePlatformWindows() g.PlatformIO.Platform_ShowWindow(viewport); } - // Clear request flags - viewport->ClearRequestFlags(); + // Position/size requests raised synchronously above need to survive until the next NewFrame(). + // Close requests keep their previous lifetime and are consumed once per platform update. + viewport->PlatformRequestClose = false; + } } @@ -19259,6 +19307,7 @@ static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWind IM_ASSERT(node->WantMouseMove == true); StartMouseMovingWindow(window); g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; + g.MovingWindowClickOffset = g.ActiveIdClickOffset; g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision. node->WantMouseMove = false; } diff --git a/imgui_internal.h b/imgui_internal.h index c0b850eaf..812f9cc31 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2498,6 +2498,7 @@ struct ImGuiContext ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiWindow* HoveredWindowBeforeClear; // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. + ImVec2 MovingWindowClickOffset; // Click offset used to position MovingWindow. May be rebased when a platform window manager adjusts our requested position. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 50da35265..3379ba754 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -11014,6 +11014,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, g.MovingWindow = docked_window; SetActiveID(g.MovingWindow->MoveId, g.MovingWindow); g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; + g.MovingWindowClickOffset = g.ActiveIdClickOffset; g.ActiveIdNoClearOnFocusLoss = true; SetActiveIdUsingAllKeyboardKeys(); }