Skip to content

Linux: only raise floating widgets during an active drag - #840

Merged
githubuser0xFFFF merged 1 commit into
githubuser0xFFFF:masterfrom
kriben:linux-endless-restacking-fix
Jun 6, 2026
Merged

Linux: only raise floating widgets during an active drag#840
githubuser0xFFFF merged 1 commit into
githubuser0xFFFF:masterfrom
kriben:linux-endless-restacking-fix

Conversation

@kriben

@kriben kriben commented Jun 5, 2026

Copy link
Copy Markdown

On Linux/X11 the CDockManager constructor connects a handler to QApplication::focusWindowChanged that, on every focus-window change, calls raise() on the dock manager, on every floating widget, and on the newly focused window. The original intent (#722) was narrower: bring the application and its floating widgets to the foreground only while a floating dock widget is being dragged over another application.

Reacting to every focus change makes this a self-sustaining loop. raise() restacks top-level windows, which on X11 can make the window manager move focus, which re-emits focusWindowChanged and re-enters the handler. Because all windows are raised on every pass, the z-order never reaches a stable state, so the windows keep restacking - visible as continuous flicker. It gets worse the more floating widgets exist, since each pass raises more windows and generates more focus transitions. The later QMainWindow/QDialog/CFloatingDockContainer type test only narrows which focus changes start the loop; once started it still runs unbounded (#785).

Fix: gate the entire raise block on a floating widget actually being dragged. Outside a drag the handler now returns immediately and issues no raise() calls, so it can no longer feed itself - no restacking, no flicker. The foreground behaviour during a real drag is unchanged and ends naturally when the drag finishes.

ADS already tracks this state internally
(FloatingDockContainerPrivate::DraggingFloatingWidget); this exposes it through a small public accessor:

bool CFloatingDockContainer::isDraggingActive() const;

Fixes #785.

On Linux/X11 the CDockManager constructor connects a handler to
QApplication::focusWindowChanged that, on every focus-window change,
calls raise() on the dock manager, on every floating widget, and on
the newly focused window. The original intent (githubuser0xFFFF#722) was narrower:
bring the application and its floating widgets to the foreground only
while a floating dock widget is being dragged over another application.

Reacting to *every* focus change makes this a self-sustaining loop.
raise() restacks top-level windows, which on X11 can make the window
manager move focus, which re-emits focusWindowChanged and re-enters the
handler. Because all windows are raised on every pass, the z-order
never reaches a stable state, so the windows keep restacking - visible
as continuous flicker. It gets worse the more floating widgets exist,
since each pass raises more windows and generates more focus
transitions. The later QMainWindow/QDialog/CFloatingDockContainer type
test only narrows which focus changes start the loop; once started it
still runs unbounded (githubuser0xFFFF#785).

Fix: gate the entire raise block on a floating widget actually being
dragged. Outside a drag the handler now returns immediately and issues
no raise() calls, so it can no longer feed itself - no restacking, no
flicker. The foreground behaviour during a real drag is unchanged and
ends naturally when the drag finishes.

ADS already tracks this state internally
(FloatingDockContainerPrivate::DraggingFloatingWidget); this exposes it
through a small public accessor:

  bool CFloatingDockContainer::isDraggingActive() const;

Fixes githubuser0xFFFF#785.
@kriben

kriben commented Jun 5, 2026

Copy link
Copy Markdown
Author

When running the SimpleExample on Ubuntu 24.04 with i3 on X11 I would get endless restacking when creating multiple floating windows: resulting in the windows figthing endlessly for focus.

I think most window managers do some form of focus-stealing prevention, and refuse or defer a programmatic raise that isn't tied to recent user input. The window gets marked "demands attention" instead of actually being restacked/focused. That refusal naturally stops the loop: the app asks to raise everything, the WM quietly declines, nothing re-fires.

i3 is strict and trusts the client's raise/focus requests instead of second-guessing them. This leads to the is a raise() -> focus-change -> raise() loop.

As a bonus this fixes #785 for me.

@githubuser0xFFFF

Copy link
Copy Markdown
Owner

Thank you very much for your contribution.

@githubuser0xFFFF
githubuser0xFFFF merged commit 27f1bef into githubuser0xFFFF:master Jun 6, 2026
ksylvan added a commit to story-wizard/Qt-Advanced-Docking-System that referenced this pull request Jul 3, 2026
…ory)

Brings in 14 upstream commits incl. dark mode support (githubuser0xFFFF#842), Wayland
drag-and-drop with in-window preview (githubuser0xFFFF#844), auto-hide title bar fix
(githubuser0xFFFF#843), Linux floating-raise fix (githubuser0xFFFF#840), LP360 double-removal fix
(githubuser0xFFFF#838), and lazy CDockWidgetTab creation for Python factories (githubuser0xFFFF#848).

Conflict resolutions:
- DockManager.cpp: keep both new includes (QTimer ours, QStyleHints upstream)
- DockOverlay.cpp: keep fork's AutoHideFeatureEnabled guard structure and
  half-panel edge-band fall-throughs; adopt upstream's GlobalPos parameter
  (new dropAreaUnderCursor(QPoint) overload) instead of QCursor::pos()
- FloatingDockContainer.cpp: line-ending false conflict (fork stores LF,
  upstream CRLF) re-merged content-wise; kept macOS escape-key polling,
  took upstream's Wayland ctor (isWayland() null-parent) + comment
- FloatingDragPreview.cpp: kept fork's dropOverlaysEnabled() early-out;
  adopted upstream's SourceContainer/Wayland container-picking loop

Validated: full build clean; QuadrantHitTestTest + OverlayGateTest pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On Linux, Dock Manager is erroneously raised on window focus changes

2 participants