From 0da25cc8238705fa8546e904898df085f9ef032e Mon Sep 17 00:00:00 2001 From: Andreas Ringlstetter Date: Tue, 17 Mar 2026 12:26:31 +0100 Subject: [PATCH] Unbind containers from DockManager to prevent accidental reuse before deletion --- src/DockAreaWidget.cpp | 3 +- src/DockContainerWidget.cpp | 11 ++++ src/DockContainerWidget.h | 3 + src/DockManager.cpp | 6 +- src/FloatingDockContainer.cpp | 114 ++++++++++++++++++---------------- 5 files changed, 78 insertions(+), 59 deletions(-) diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index 286c11276..1f76c71b5 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -609,8 +609,7 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget) { if(CFloatingDockContainer* FloatingDockContainer = DockContainer->floatingWidget()) { - FloatingDockContainer->hide(); - FloatingDockContainer->deleteLater(); + FloatingDockContainer->finishDropOperation(); } } } diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index faee59819..6639275a8 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -2217,6 +2217,17 @@ CDockManager* CDockContainerWidget::dockManager() const } +//=========================================================================== +void CDockContainerWidget::removeFromDockManager() +{ + if (d->DockManager) + { + d->DockManager->removeDockContainer(this); + d->DockManager.clear(); + } +} + + //=========================================================================== void CDockContainerWidget::handleAutoHideWidgetEvent(QEvent* e, QWidget* w) { diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index d32824b39..1667b86e9 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -86,6 +86,9 @@ class ADS_EXPORT CDockContainerWidget : public QFrame friend AutoHideDockContainerPrivate; friend CAutoHideSideBar; +private Q_SLOTS: + void removeFromDockManager(); + protected: /** * Handles activation events to update zOrderIndex diff --git a/src/DockManager.cpp b/src/DockManager.cpp index bc1326bae..605d0e3f5 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -768,7 +768,8 @@ void CDockManager::registerFloatingWidget(CFloatingDockContainer* FloatingWidget //============================================================================ void CDockManager::removeFloatingWidget(CFloatingDockContainer* FloatingWidget) { - d->FloatingWidgets.removeAll(FloatingWidget); + int removed = d->FloatingWidgets.removeAll(FloatingWidget); + Q_ASSERT(removed == 1); } //============================================================================ @@ -783,7 +784,8 @@ void CDockManager::removeDockContainer(CDockContainerWidget* DockContainer) { if (this != DockContainer) { - d->Containers.removeAll(DockContainer); + int removed = d->Containers.removeAll(DockContainer); + Q_ASSERT(removed == 1); } } diff --git a/src/FloatingDockContainer.cpp b/src/FloatingDockContainer.cpp index b71e2e401..257974caa 100644 --- a/src/FloatingDockContainer.cpp +++ b/src/FloatingDockContainer.cpp @@ -370,14 +370,14 @@ struct FloatingDockContainerPrivate eDragState DraggingState = DraggingInactive; QPoint DragStartMousePosition; CDockContainerWidget *DropContainer = nullptr; - CDockAreaWidget *SingleDockArea = nullptr; - QPoint DragStartPos; - bool Hiding = false; - bool AutoHideChildren = true; - bool HideContentOnNextHide = false; -#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) - QWidget* MouseEventHandler = nullptr; - CFloatingWidgetTitleBar* TitleBar = nullptr; + CDockAreaWidget *SingleDockArea = nullptr; + QPoint DragStartPos; + bool Hiding = false; + bool AutoHideChildren = true; + bool HideContentOnNextHide = false; +#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) + QWidget* MouseEventHandler = nullptr; + CFloatingWidgetTitleBar* TitleBar = nullptr; bool IsResizing = false; bool MousePressed = false; #endif @@ -500,6 +500,9 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent() return; } + // DockManager will be unlinked from this within DropContainer->dropFloatingWidget + const auto OriginalDockManager = this->DockManager.data(); + if (DockManager->dockAreaOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea || DockManager->containerOverlay()->dropAreaUnderCursor() != InvalidDockWidgetArea) { @@ -533,8 +536,8 @@ void FloatingDockContainerPrivate::titleMouseReleaseEvent() DropContainer->dropFloatingWidget(_this, QCursor::pos()); } - DockManager->containerOverlay()->hideOverlay(); - DockManager->dockAreaOverlay()->hideOverlay(); + OriginalDockManager->containerOverlay()->hideOverlay(); + OriginalDockManager->dockAreaOverlay()->hideOverlay(); } @@ -928,11 +931,11 @@ bool CFloatingDockContainer::nativeEvent(const QByteArray &eventType, void *mess //============================================================================ -void CFloatingDockContainer::closeEvent(QCloseEvent *event) -{ - ADS_PRINT("CFloatingDockContainer closeEvent"); - d->setState(DraggingInactive); - event->ignore(); +void CFloatingDockContainer::closeEvent(QCloseEvent *event) +{ + ADS_PRINT("CFloatingDockContainer closeEvent"); + d->setState(DraggingInactive); + event->ignore(); if (!isClosable()) { return; @@ -960,14 +963,14 @@ void CFloatingDockContainer::closeEvent(QCloseEvent *event) return; } - // New bug (QWebEngineView reload side effect): - // when a WebEngine-based dock is tabified into a floating container, the - // embedded native/web process can trigger delayed hide/show cycles on the - // floating window. If every non-spontaneous hide propagates to - // DockWidget->toggleView(false), unrelated tabs are marked closed and seem + // New bug (QWebEngineView reload side effect): + // when a WebEngine-based dock is tabified into a floating container, the + // embedded native/web process can trigger delayed hide/show cycles on the + // floating window. If every non-spontaneous hide propagates to + // DockWidget->toggleView(false), unrelated tabs are marked closed and seem // to "disappear". We therefore arm HideContentOnNextHide only for the - // explicit close path. - d->HideContentOnNextHide = true; + // explicit close path. + d->HideContentOnNextHide = true; // In Qt version after 5.9.2 there seems to be a bug that causes the // QWidget::event() function to not receive any NonClientArea mouse @@ -975,40 +978,40 @@ void CFloatingDockContainer::closeEvent(QCloseEvent *event) // https://bugreports.qt.io/browse/QTBUG-73295 // The following code is a workaround for Qt versions > 5.9.2 that seems // to work - // Starting from Qt version 5.12.2 this seems to work again. But - // now the QEvent::NonClientAreaMouseButtonPress function returns always - // Qt::RightButton even if the left button was pressed - this->hide(); -} - -//============================================================================ -void CFloatingDockContainer::hideEvent(QHideEvent *event) -{ - Super::hideEvent(event); - if (event->spontaneous()) - { - return; - } + // Starting from Qt version 5.12.2 this seems to work again. But + // now the QEvent::NonClientAreaMouseButtonPress function returns always + // Qt::RightButton even if the left button was pressed + this->hide(); +} + +//============================================================================ +void CFloatingDockContainer::hideEvent(QHideEvent *event) +{ + Super::hideEvent(event); + if (event->spontaneous()) + { + return; + } // Prevent toogleView() events during restore state - if (d->DockManager->isRestoringState()) - { - return; - } - - // Only a close operation should propagate hide->toggleView(false) to - // child dock widgets. Generic hide/show cycles (e.g. from platform or - // embedded native content) must not change dock open/closed state. - if (!d->HideContentOnNextHide) - { - return; - } - d->HideContentOnNextHide = false; - - if ( d->AutoHideChildren ) - { - d->Hiding = true; - for ( auto DockArea : d->DockContainer->openedDockAreas() ) + if (d->DockManager->isRestoringState()) + { + return; + } + + // Only a close operation should propagate hide->toggleView(false) to + // child dock widgets. Generic hide/show cycles (e.g. from platform or + // embedded native content) must not change dock open/closed state. + if (!d->HideContentOnNextHide) + { + return; + } + d->HideContentOnNextHide = false; + + if ( d->AutoHideChildren ) + { + d->Hiding = true; + for ( auto DockArea : d->DockContainer->openedDockAreas() ) { for ( auto DockWidget : DockArea->openedDockWidgets() ) { @@ -1220,8 +1223,9 @@ void CFloatingDockContainer::finishDropOperation() if (d->DockManager) { d->DockManager->removeFloatingWidget(this); - d->DockManager->removeDockContainer(this->dockContainer()); + d->DockManager.clear(); } + this->dockContainer()->removeFromDockManager(); } //============================================================================