diff --git a/src/DockWidgetBase.cpp b/src/DockWidgetBase.cpp index f0b84e59..caf8bccd 100644 --- a/src/DockWidgetBase.cpp +++ b/src/DockWidgetBase.cpp @@ -551,10 +551,17 @@ MDILayoutWidget *DockWidgetBase::Private::mdiLayout() const bool DockWidgetBase::Private::isMDIWrapper() const { - if (auto dropAreaGuest = qobject_cast(q->widget())) - return dropAreaGuest->isMDIWrapper(); + return mdiDropAreaWrapper() != nullptr; +} - return false; +DropArea *DockWidgetBase::Private::mdiDropAreaWrapper() const +{ + if (auto dropAreaGuest = qobject_cast(q->widget())) { + if (dropAreaGuest->isMDIWrapper()) + return dropAreaGuest; + } + + return nullptr; } DockWidgetBase::Private *DockWidgetBase::dptr() const diff --git a/src/private/DockWidgetBase_p.h b/src/private/DockWidgetBase_p.h index 331e0619..3254b8e3 100644 --- a/src/private/DockWidgetBase_p.h +++ b/src/private/DockWidgetBase_p.h @@ -140,6 +140,9 @@ public: /// This is only used by the DockWidget::Option_MDINestable feature bool isMDIWrapper() const; + /// @brief If this dock widget is an MDI wrapper (isMDIWrapper()), then returns the wrapper drop area + DropArea *mdiDropAreaWrapper() const; + const QString name; QStringList affinities; QString title; diff --git a/src/private/FloatingWindow.cpp b/src/private/FloatingWindow.cpp index 1c2260cb..9128e191 100644 --- a/src/private/FloatingWindow.cpp +++ b/src/private/FloatingWindow.cpp @@ -22,6 +22,7 @@ #include "FrameworkWidgetFactory.h" #include "DragController_p.h" #include "LayoutSaver_p.h" +#include "DockWidgetBase_p.h" #include #include @@ -146,10 +147,26 @@ FloatingWindow::FloatingWindow(Frame *frame, QRect suggestedGeometry, MainWindow { QScopedValueRollback guard(m_disableSetVisible, true); - // Adding a widget will trigger onFrameCountChanged, which triggers a setVisible(true). - // The problem with setVisible(true) will forget about or requested geometry and place the window at 0,0 - // So disable the setVisible(true) call while in the ctor. - m_dropArea->addWidget(frame, KDDockWidgets::Location_OnTop, {}); + if (frame->hasNestedMDIDockWidgets()) { + // When using DockWidget::MDINestable, the docked MDI widget is wrapped by a drop area so we can drop things into it. + // When floating it, we can delete that helper drop area, as FloatingWindow already has one + + if (frame->dockWidgetCount() == 0) { + // doesn't happen + qWarning() << Q_FUNC_INFO << "Unexpected empty frame"; + return; + } + + auto dwMDIWrapper = frame->dockWidgetAt(0); + auto dropAreaMDIWrapper = dwMDIWrapper->d->mdiDropAreaWrapper(); + m_dropArea->addMultiSplitter(dropAreaMDIWrapper, Location_OnTop); + delete dwMDIWrapper; + } else { + // Adding a widget will trigger onFrameCountChanged, which triggers a setVisible(true). + // The problem with setVisible(true) will forget about or requested geometry and place the window at 0,0 + // So disable the setVisible(true) call while in the ctor. + m_dropArea->addWidget(frame, KDDockWidgets::Location_OnTop, {}); + } } FloatingWindow::~FloatingWindow() diff --git a/src/private/Frame.cpp b/src/private/Frame.cpp index 4492e5e2..f184364f 100644 --- a/src/private/Frame.cpp +++ b/src/private/Frame.cpp @@ -889,6 +889,19 @@ MDILayoutWidget *Frame::mdiLayoutWidget() const return qobject_cast(m_layoutWidget); } +bool Frame::hasNestedMDIDockWidgets() const +{ + if (!isMDI() || dockWidgetCount() == 0) + return false; + + if (dockWidgetCount() != 1) { + qWarning() << Q_FUNC_INFO << "Expected a single dock widget wrapper as frame child"; + return false; + } + + return dockWidgetAt(0)->d->isMDIWrapper(); +} + int Frame::userType() const { return m_userType; diff --git a/src/private/Frame_p.h b/src/private/Frame_p.h index 946c96aa..03288a56 100644 --- a/src/private/Frame_p.h +++ b/src/private/Frame_p.h @@ -302,6 +302,10 @@ public: /// @brief Returns the MDI layout. Or nullptr if this frame isn't in a MDI layout MDILayoutWidget *mdiLayoutWidget() const; + /// @brief If this frame is a MDI frame (isMDI() == true), returns whether it contains nested dock widgets (DockWidget::Option_MDINestable) + /// @sa isMDI() + bool hasNestedMDIDockWidgets() const; + /// @brief See DockWidgetBase::userType() int userType() const; diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 12449b9c..e69ba9e7 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -5153,7 +5153,32 @@ void TestDocks::tst_mdi_mixed_with_docking2() QVERIFY(!mdiWidget3->isOpen()); QVERIFY(dropArea1.isNull()); - // QTest::qWait(10000); + // Reopen everything again: + + mdiArea->addDockWidget(mdiWidget1, QPoint(10, 10)); + mdiArea->addDockWidget(mdiWidget2, QPoint(50, 50)); + frame1 = mdiWidget1->d->frame(); + mdiFrame1 = frame1->mdiFrame(); + dropArea1 = frame1->mdiDropAreaWrapper(); + dropArea1->addDockWidget(mdiWidget3, Location_OnLeft, nullptr); + + // Test floating: + frame2 = mdiWidget2->d->frame(); + QPointer dwWrapper2 = frame2->mdiDockWidgetWrapper(); + dropArea2 = frame2->mdiDropAreaWrapper(); + QVERIFY(mdiWidget2->isVisible()); + QVERIFY(frame2->isMDIWrapper()); + QVERIFY(dwWrapper2->d->isMDIWrapper()); + mdiWidget2->setFloating(true); + QVERIFY(mdiWidget2->isFloating()); + + QVERIFY(!mdiWidget2->d->frame()->isMDI()); + QVERIFY(!mdiWidget2->d->frame()->isMDIWrapper()); + QTest::qWait(500); // remove + QVERIFY(dropArea2.isNull()); + QVERIFY(dwWrapper2.isNull()); + + // QTest::qWait(100000); } // No need to port to QtQuick