Make DockWidgetBase::setMDIPosition|setMDISize support nesting

They now honour Option_MDINestable.
Before they would bail out assuming they weren't in a MDI area.
Now they look further up the hierarchy to find our MDIArea, if any.
This commit is contained in:
Sergio Martins
2022-01-25 20:14:04 +00:00
parent 2b1aa44eff
commit d0dcac6b03
6 changed files with 116 additions and 8 deletions

View File

@@ -543,8 +543,30 @@ void DockWidgetBase::Private::maybeMorphIntoFloatingWindow()
MDILayoutWidget *DockWidgetBase::Private::mdiLayout() const
{
if (auto mw = mainWindow())
return mw->mdiLayoutWidget();
auto p = const_cast<QObject *>(q->parent());
while (p) {
if (qobject_cast<const QWindow*>(p)) {
// Ignore QObject hierarchies spanning though multiple windows
return nullptr;
}
if (auto layout = qobject_cast<LayoutWidget*>(p)) {
// We found a layout
if (auto mdiLayout = qobject_cast<MDILayoutWidget*>(p)) {
// And it's MDI
return mdiLayout;
} else if (auto dropArea = qobject_cast<DropArea*>(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<QObject *>(q->parent());
while (p) {
if (qobject_cast<const QWindow*>(p)) {
// Ignore QObject hierarchies spanning though multiple windows
return nullptr;
}
if (auto layout = qobject_cast<LayoutWidget*>(p)) {
if (auto dropArea = qobject_cast<DropArea*>(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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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()
{

View File

@@ -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();