#9398

Secondary viewports clamped to monitor boundary during drag; content shift when OS refuses position (Linux/X11)

Opengeonheekimopened 6mo ago

Version/Branch of Dear ImGui:

Version 1.92.8 WIP, Branch: docking

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

Linux Ubuntu 22.04, GCC 11.4.0, X11/GNOME Shell, kernel 6.8.0

Full config/build information:

ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_ViewportsEnable

Details:

My Issue / Question:

When a window is undocked into a secondary viewport (a separate OS window), it cannot be dragged beyond the monitor edge — it hits an invisible wall — even though the main GLFW window and other native apps (e.g. VSCode) can freely cross that boundary.

Additionally, while the secondary viewport is at or near the monitor edge, the rendered content (e.g. a crosshair drawn at mouse coordinates) appears visually offset from the actual cursor position.


Root cause (traced in source):

  1. Monitor-edge wall — ClampWindowPos in imgui.cpp (around the window->ViewportOwned branch, ~line 8257–8272) limits the window position to monitor->WorkPos + monitor->WorkSize expanded by -visibility_padding (default DisplayWindowPadding = {19, 19}). The PlatformMonitorsFullWorkRect path that allows straddling (#7299, #3071) is only entered when g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree. During a viewport-owned drag this condition is met, but visibility_rect is then expanded by -visibility_padding which still applies 19 px of clamping — the window can get close to the edge but not past it.

  2. Content shift — NewFrame() sets viewport->Pos to the desired position (cursor − grab delta, which may be beyond the monitor edge). TranslateWindowsInViewport has already shifted imgui child windows to LastPlatformPos (the actual OS position, clamped by the WM). So after NewFrame():

    • ImGui child-window global coordinates are based on P_actual
    • viewport->Pos (== DisplayPos) equals P_desired
    • Result: every pixel in the viewport is rendered with a P_actual − P_desired offset → crosshair and mouse-position overlays appear shifted by the "overshoot" amount.

Expected behavior:

Secondary viewports should be draggable beyond the monitor edge the same way the main window is (the OS/WM ultimately controls whether the window actually moves). Rendered content should never appear shifted relative to the cursor while dragging near a monitor boundary.


Steps to reproduce:

  1. Enable ImGuiConfigFlags_ViewportsEnable.
  2. Undock any window so it becomes a secondary OS window.
  3. Drag the secondary window toward and past the monitor edge. → Window stops at ~19 px from the edge (the DisplayWindowPadding limit).
  4. While it is stopped at the edge, content rendered at io.MousePos (e.g. a crosshair) appears shifted by the amount the drag "wanted" to move the window further.

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

// Standard docking+viewports example (Dear ImGui demo is sufficient to reproduce). // 1. Run imgui_demo with viewports enabled on Linux/X11. // 2. Undock "Dear ImGui Demo" window (drag it out of the main window). // 3. Draw a crosshair at io.MousePos inside the secondary window's Begin/End. // 4. Drag the secondary window to a monitor edge. // Observe: window stops moving before the edge; crosshair shifts from cursor.

ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once); ImGui::Begin("Viewport test"); ImVec2 p = ImGui::GetMousePos(); ImGui::GetForegroundDrawList()->AddLine({p.x - 10, p.y}, {p.x + 10, p.y}, IM_COL32(255, 0, 0, 255)); ImGui::GetForegroundDrawList()->AddLine({p.x, p.y - 10}, {p.x, p.y + 10}, IM_COL32(255, 0, 0, 255)); ImGui::End();

Activity

0

No activity yet.