diff --git a/src/DockWidgetBase.cpp b/src/DockWidgetBase.cpp index caf8bccd..e3638e45 100644 --- a/src/DockWidgetBase.cpp +++ b/src/DockWidgetBase.cpp @@ -543,8 +543,30 @@ void DockWidgetBase::Private::maybeMorphIntoFloatingWindow() MDILayoutWidget *DockWidgetBase::Private::mdiLayout() const { - if (auto mw = mainWindow()) - return mw->mdiLayoutWidget(); + auto p = const_cast(q->parent()); + while (p) { + if (qobject_cast(p)) { + // Ignore QObject hierarchies spanning though multiple windows + return nullptr; + } + + if (auto layout = qobject_cast(p)) { + // We found a layout + if (auto mdiLayout = qobject_cast(p)) { + // And it's MDI + return mdiLayout; + } else if (auto dropArea = qobject_cast(p)) { + // It's a DropArea. But maybe it's a drop area that's just helping + // making the MDI windows accept drops (Option_MDINestable) + if (!dropArea->isMDIWrapper()) + return nullptr; + + // It's a MDI wrapper, keep looking up. + } + } + + p = p->parent(); + } return nullptr; } @@ -564,6 +586,36 @@ DropArea *DockWidgetBase::Private::mdiDropAreaWrapper() const return nullptr; } +DockWidgetBase *DockWidgetBase::Private::mdiDockWidgetWrapper() const +{ + if (isMDIWrapper()) { + // We are the wrapper + return q; + } + + auto p = const_cast(q->parent()); + while (p) { + if (qobject_cast(p)) { + // Ignore QObject hierarchies spanning though multiple windows + return nullptr; + } + + if (auto layout = qobject_cast(p)) { + if (auto dropArea = qobject_cast(p)) { + if (dropArea->isMDIWrapper()) + return dropArea->mdiDockWidgetWrapper(); + + } + + return nullptr; + } + + p = p->parent(); + } + + return nullptr; +} + DockWidgetBase::Private *DockWidgetBase::dptr() const { return d; @@ -852,14 +904,26 @@ int DockWidgetBase::userType() const void DockWidgetBase::setMDIPosition(QPoint pos) { - if (MDILayoutWidget *layout = d->mdiLayout()) - layout->moveDockWidget(this, pos); + if (MDILayoutWidget *layout = d->mdiLayout()) { + if (auto wrapperDW = d->mdiDockWidgetWrapper()) { + // Case of using Option_MDINestable. We need to layout the actual top level DW + layout->moveDockWidget(wrapperDW, pos); + } else { + layout->moveDockWidget(this, pos); + } + } } void DockWidgetBase::setMDISize(QSize size) { - if (MDILayoutWidget *layout = d->mdiLayout()) - layout->resizeDockWidget(this, size); + if (MDILayoutWidget *layout = d->mdiLayout()) { + if (auto wrapperDW = d->mdiDockWidgetWrapper()) { + // Case of using Option_MDINestable. We need to layout the actual top level DW + layout->resizeDockWidget(wrapperDW, size); + } else { + layout->resizeDockWidget(this, size); + } + } } void DockWidgetBase::setMDIZ(int z) diff --git a/src/private/DockWidgetBase_p.h b/src/private/DockWidgetBase_p.h index 3254b8e3..e07e253f 100644 --- a/src/private/DockWidgetBase_p.h +++ b/src/private/DockWidgetBase_p.h @@ -143,6 +143,10 @@ public: /// @brief If this dock widget is an MDI wrapper (isMDIWrapper()), then returns the wrapper drop area DropArea *mdiDropAreaWrapper() const; + /// @brief If this dock widget is inside a drop area nested in MDI then returns the wrapper dock widget + /// This goes up the hierarchy, while mdiDropAreaWrapper goes down. + DockWidgetBase *mdiDockWidgetWrapper() const; + const QString name; QStringList affinities; QString title; diff --git a/src/private/DropArea_p.h b/src/private/DropArea_p.h index 2bc47354..5c1063ac 100644 --- a/src/private/DropArea_p.h +++ b/src/private/DropArea_p.h @@ -74,6 +74,9 @@ public: /// When DockWidget::Option_MDINestable is used, docked MDI dock widgets will be wrapped inside a DropArea, so they accept drops /// This DropArea is created implicitly while docking, and this function will return true bool isMDIWrapper() const; + + /// Returns the helper dock widget for implementing DockWidget::Option_MDINestable. + DockWidgetBase *mdiDockWidgetWrapper() const; private: Q_DISABLE_COPY(DropArea) friend class Frame; @@ -88,7 +91,6 @@ private: bool drop(QWidgetOrQuick *droppedwindow, KDDockWidgets::Location location, Frame *relativeTo); Frame *frameContainingPos(QPoint globalPos) const; void updateFloatingActions(); - DockWidgetBase *mdiDockWidgetWrapper() const; bool m_inDestructor = false; const bool m_isMDIWrapper; diff --git a/src/private/MDILayoutWidget.cpp b/src/private/MDILayoutWidget.cpp index fdd827e6..6a26b277 100644 --- a/src/private/MDILayoutWidget.cpp +++ b/src/private/MDILayoutWidget.cpp @@ -107,7 +107,8 @@ void MDILayoutWidget::resizeDockWidget(Frame *frame, QSize size) Layouting::Item *item = itemForFrame(frame); if (!item) { - qWarning() << Q_FUNC_INFO << "Frame not found in the layout" << frame; + qWarning() << Q_FUNC_INFO << "Frame not found in the layout" << frame << frame->isMDI() + << frame->isMDIWrapper(); return; } diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 62c22de7..e1b5df54 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -5213,6 +5213,42 @@ void TestDocks::tst_mdi_mixed_with_docking2() mdiTitleBar->onFloatClicked(); } +void TestDocks::tst_mdi_mixed_with_docking_setMDISize() +{ + EnsureTopLevelsDeleted e; + auto m = createMainWindow(QSize(1000, 500), MainWindowOption_HasCentralWidget); + auto dock1 = createDockWidget("1", new QPushButton("1")); + + m->addDockWidget(dock1, Location_OnBottom); + + auto mdiArea = new MDIArea(); + m->setPersistentCentralWidget(mdiArea); + + auto createSheet = [](int id) -> DockWidgetBase* { + auto dock = new DockWidget(QStringLiteral("dw-sheet-%1").arg(id), DockWidgetBase::Option_MDINestable); + dock->setWidget(new QPushButton(QStringLiteral("Sheet %1").arg(id))); + dock->setTitle(QStringLiteral("Sheet %1").arg(id)); + + return dock; + }; + + auto mdiWidget1 = createSheet(1); + auto mdiWidget2 = createSheet(2); + + mdiArea->addDockWidget(mdiWidget1, QPoint(10, 10)); + mdiArea->addDockWidget(mdiWidget2, QPoint(50, 50)); + + Frame *frame1 = mdiArea->frames().at(0); + + const QSize sz1 = frame1->QWidgetAdapter::size(); + const QSize increment(200, 200); + + QVERIFY(mdiWidget1->d->mdiLayout()); + mdiWidget1->setMDISize(sz1 + increment); + + QCOMPARE(frame1->QWidgetAdapter::size(), sz1 + increment); +} + // No need to port to QtQuick void TestDocks::tst_floatingWindowDeleted() { diff --git a/tests/tst_docks.h b/tests/tst_docks.h index 2b57ef4a..c199f340 100644 --- a/tests/tst_docks.h +++ b/tests/tst_docks.h @@ -252,6 +252,7 @@ private Q_SLOTS: void tst_mainWindowAlwaysHasCentralWidget(); void tst_dockableMainWindows(); void tst_mdi_mixed_with_docking2(); + void tst_mdi_mixed_with_docking_setMDISize(); // But these are fine to be widget only: void tst_tabsNotClickable();