From fed9f7cbd982dd38bf51004761e5214e6f219038 Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Fri, 5 Jun 2026 09:22:29 +0200 Subject: [PATCH] Linux: only raise floating widgets during an active drag 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. --- src/DockManager.cpp | 20 ++++++++++---------- src/FloatingDockContainer.cpp | 6 ++++++ src/FloatingDockContainer.h | 5 +++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/DockManager.cpp b/src/DockManager.cpp index a7c639e57..0719e702a 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -548,16 +548,16 @@ CDockManager::CDockManager(QWidget *parent) : return; } - // If the user clicks the main window or drags a floating widget or works with a - // modal dialog, then raise the main window, all floating widgets and the focus window - // itself to bring it into foreground of any other application. - bool raise = qobject_cast(widget) - || qobject_cast(widget); - if (auto dialog = qobject_cast(widget)) - { - raise |= dialog->isModal(); - } - if (!raise) + // Only restack windows while a floating dock widget is actively being dragged. + // Reacting to ordinary focus changes makes this handler raise() multiple + // top-level windows, which can transfer focus and re-emit focusWindowChanged, + // re-entering this handler in a self-sustaining loop that never settles - + // visible as constant flicker on Linux/X11. Gating on an + // active drag breaks that loop: outside a drag we do nothing. + const bool draggingActive = std::any_of( + d->FloatingWidgets.begin(), d->FloatingWidgets.end(), + [](CFloatingDockContainer* fw){ return fw && fw->isDraggingActive(); }); + if (!draggingActive) { return; } diff --git a/src/FloatingDockContainer.cpp b/src/FloatingDockContainer.cpp index 257974caa..408bbbf0a 100644 --- a/src/FloatingDockContainer.cpp +++ b/src/FloatingDockContainer.cpp @@ -817,6 +817,12 @@ CDockContainerWidget* CFloatingDockContainer::dockContainer() const return d->DockContainer; } +//============================================================================ +bool CFloatingDockContainer::isDraggingActive() const +{ + return d->isState(DraggingFloatingWidget); +} + //============================================================================ void CFloatingDockContainer::changeEvent(QEvent *event) { diff --git a/src/FloatingDockContainer.h b/src/FloatingDockContainer.h index 284e30ad5..52991cc29 100644 --- a/src/FloatingDockContainer.h +++ b/src/FloatingDockContainer.h @@ -269,6 +269,11 @@ private Q_SLOTS: */ void finishDropOperation(); + /** + * Returns true while this floating widget is actively being dragged. + */ + bool isDraggingActive() const; + #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) /** * This is a function that responds to FloatingWidgetTitleBar::maximizeRequest()