diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index d866626b427..60878462f42 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -1797,6 +1797,7 @@ QAction * QDockWidget::toggleViewAction() const dock \a area, or is moved to a different location in its current dock area. This happens when the dock widget is moved programmatically or is dragged to a new location by the user. + \sa dockLocation(), setDockLocation() */ /*! @@ -1854,6 +1855,51 @@ void QDockWidget::setTitleBarWidget(QWidget *widget) } } +/*! + \since 6.9 + \brief QDockWidget::setDockLocation(): Assign dock widget to \a area. + If docked at another dock location, it will move to \a area. + If floating or part of floating tabs, the next call of setFloating(false) + will dock it at \a area. + + \note + setDockLocation(Qt::NoDockLocation) is equivalent to setFloating(true). + \sa dockLocation(), dockLocationChanged() + */ +void QDockWidget::setDockLocation(Qt::DockWidgetArea area) +{ + if (area == Qt::NoDockWidgetArea && !isFloating()) { + setFloating(true); + return; + } + + auto *mainWindow = const_cast(mainwindow_from_dock(this)); + Q_ASSERT(mainWindow); + mainWindow->addDockWidget(area, this); +} + +/*! + \since 6.9 + \brief QDockWidget::dockLocation() + \return the current dock location, or Qt::NoDockLocation if it's floating + and/or has no mainwindow parent. + \sa setDockLocation(), dockLocationChanged() + */ +Qt::DockWidgetArea QDockWidget::dockLocation() const +{ + // QDockWidgetPrivate::setWindowState() emits NoDockWidgetArea if + // the dock widget becomes floating. + // QMainWindowLayout::dockWidgetArea() always returns the area where + // the dock widget's item_list is kept. + if (isFloating()) + return Qt::NoDockWidgetArea; + + auto *mainWindow = mainwindow_from_dock(this); + Q_ASSERT(mainWindow); + // FIXME in Qt 7: Make dockWidgetArea take a const QDockWidget* argument + return mainWindow->dockWidgetArea(const_cast(this)); +} + /*! \since 4.3 Returns the custom title bar widget set on the QDockWidget, or diff --git a/src/widgets/widgets/qdockwidget.h b/src/widgets/widgets/qdockwidget.h index 2efa1d3c2f3..7383b356b15 100644 --- a/src/widgets/widgets/qdockwidget.h +++ b/src/widgets/widgets/qdockwidget.h @@ -25,6 +25,8 @@ class Q_WIDGETS_EXPORT QDockWidget : public QWidget Q_PROPERTY(Qt::DockWidgetAreas allowedAreas READ allowedAreas WRITE setAllowedAreas NOTIFY allowedAreasChanged) Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle DESIGNABLE true) + Q_PROPERTY(Qt::DockWidgetArea dockLocation READ dockLocation WRITE setDockLocation + NOTIFY dockLocationChanged) public: explicit QDockWidget(const QString &title, QWidget *parent = nullptr, @@ -61,6 +63,9 @@ public: void setTitleBarWidget(QWidget *widget); QWidget *titleBarWidget() const; + void setDockLocation(Qt::DockWidgetArea area); + Qt::DockWidgetArea dockLocation() const; + inline bool isAreaAllowed(Qt::DockWidgetArea area) const { return (allowedAreas() & area) == area; } diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp index 11d4a40fe61..f1165ea3297 100644 --- a/src/widgets/widgets/qmainwindow.cpp +++ b/src/widgets/widgets/qmainwindow.cpp @@ -1037,8 +1037,11 @@ void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget default: break; } + const Qt::DockWidgetArea oldArea = dockWidgetArea(dockwidget); d_func()->layout->removeWidget(dockwidget); // in case it was already in here addDockWidget(area, dockwidget, orientation); + if (oldArea != area) + emit dockwidget->dockLocationChanged(area); } /*! diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index c72ae0b539d..2cc7f585d09 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -1710,6 +1710,10 @@ QRect QMainWindowLayout::dockWidgetAreaRect(const Qt::DockWidgetArea area, DockW return (size == Maximum) ? dl.gapRect(dockPosition) : dl.docks[dockPosition].rect; } +/*! + \internal + Add \a dockwidget to \a area in \a orientation. + */ void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget, Qt::Orientation orientation) @@ -1722,7 +1726,6 @@ void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area, endSeparatorMove(movingSeparatorPos); layoutState.dockAreaLayout.addDockWidget(toDockPos(area), dockwidget, orientation); - emit dockwidget->dockLocationChanged(area); invalidate(); } diff --git a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp index ea7f6d5bc99..1762676ebdb 100644 --- a/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp +++ b/tests/auto/widgets/widgets/qdockwidget/tst_qdockwidget.cpp @@ -756,6 +756,12 @@ void tst_QDockWidget::updateTabBarOnVisibilityChanged() Q_DECLARE_METATYPE(Qt::DockWidgetArea) +Qt::DockWidgetArea dockLocation(const QSignalSpy *spy) +{ + Q_ASSERT(spy); + return qvariant_cast(spy->at(0).at(0)); +} + void tst_QDockWidget::dockLocationChanged() { qRegisterMetaType("Qt::DockWidgetArea"); @@ -763,61 +769,66 @@ void tst_QDockWidget::dockLocationChanged() QMainWindow mw; QDockWidget dw; dw.setObjectName("dock1"); - QSignalSpy spy(&dw, SIGNAL(dockLocationChanged(Qt::DockWidgetArea))); + QSignalSpy spy(&dw, &QDockWidget::dockLocationChanged); mw.addDockWidget(Qt::LeftDockWidgetArea, &dw); QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::LeftDockWidgetArea); + QCOMPARE(dockLocation(&spy), Qt::LeftDockWidgetArea); + + constexpr std::array areas{Qt::LeftDockWidgetArea, + Qt::TopDockWidgetArea, + Qt::RightDockWidgetArea, + Qt::BottomDockWidgetArea, + Qt::NoDockWidgetArea}; + + for (const auto area : areas) { + spy.clear(); + const int expectedCount = dw.dockLocation() == area ? 0 : 1; + dw.setDockLocation(area); + + // Ensure signal is only fired on changes + QCOMPARE(spy.count(), expectedCount); + if (expectedCount) + QCOMPARE(dockLocation(&spy), area); + + // Ensure getter reports correctly + QTRY_COMPARE(dw.dockLocation(), area); + + // Ensure setting NoDockWidgetArea floats the dock widget + if (area == Qt::NoDockWidgetArea) { + QCOMPARE(dw.isFloating(), true); + dw.setFloating(false); + } + } + spy.clear(); - - mw.addDockWidget(Qt::LeftDockWidgetArea, &dw); - QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::LeftDockWidgetArea); - spy.clear(); - - mw.addDockWidget(Qt::RightDockWidgetArea, &dw); - QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::RightDockWidgetArea); - spy.clear(); - - mw.removeDockWidget(&dw); - QCOMPARE(spy.size(), 0); - QDockWidget dw2; dw2.setObjectName("dock2"); mw.addDockWidget(Qt::TopDockWidgetArea, &dw2); mw.tabifyDockWidget(&dw2, &dw); QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::TopDockWidgetArea); + QCOMPARE(dockLocation(&spy), Qt::TopDockWidgetArea); spy.clear(); mw.splitDockWidget(&dw2, &dw, Qt::Horizontal); QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::TopDockWidgetArea); + QCOMPARE(dockLocation(&spy), Qt::TopDockWidgetArea); spy.clear(); dw.setFloating(true); QTRY_COMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::NoDockWidgetArea); + QCOMPARE(dockLocation(&spy), Qt::NoDockWidgetArea); spy.clear(); dw.setFloating(false); QTRY_COMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::TopDockWidgetArea); + QCOMPARE(dockLocation(&spy), Qt::TopDockWidgetArea); spy.clear(); QByteArray ba = mw.saveState(); mw.restoreState(ba); QCOMPARE(spy.size(), 1); - QCOMPARE(qvariant_cast(spy.at(0).at(0)), - Qt::TopDockWidgetArea); + QCOMPARE(dockLocation(&spy), Qt::TopDockWidgetArea); } void tst_QDockWidget::setTitleBarWidget()