QDockWidget: add Q_PROPERTY dockWidgetArea

Implement dockLocation Q_PROPERTY. Use dockLocationChanged signal.
Ensure the signal is only emitted on changes.
Add and document getter and setter.
Modify tst_QDockWidget::dockLocationChanged() to cover new API.

[ChangeLog][QtWidgets][QDockWidget] Added dockLocation Q_PROPERTY
with new dockLocation() and setDockLocation() API.

Fixes: QTBUG-117834
Change-Id: I85cfb613ae932f9e0dd42f8e7b20fee91a4201e6
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Axel Spoerl 2024-11-18 16:08:54 +01:00
parent da61af8065
commit cbd2f56c14
5 changed files with 98 additions and 30 deletions

View File

@ -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<QMainWindow *>(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<QDockWidget *>(this));
}
/*!
\since 4.3
Returns the custom title bar widget set on the QDockWidget, or

View File

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

View File

@ -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);
}
/*!

View File

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

View File

@ -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<Qt::DockWidgetArea>(spy->at(0).at(0));
}
void tst_QDockWidget::dockLocationChanged()
{
qRegisterMetaType<Qt::DockWidgetArea>("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<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::LeftDockWidgetArea);
QCOMPARE(dockLocation(&spy), Qt::LeftDockWidgetArea);
constexpr std::array<Qt::DockWidgetArea, 5> 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);
}
}
mw.addDockWidget(Qt::LeftDockWidgetArea, &dw);
QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::LeftDockWidgetArea);
spy.clear();
mw.addDockWidget(Qt::RightDockWidgetArea, &dw);
QCOMPARE(spy.size(), 1);
QCOMPARE(qvariant_cast<Qt::DockWidgetArea>(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<Qt::DockWidgetArea>(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<Qt::DockWidgetArea>(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<Qt::DockWidgetArea>(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<Qt::DockWidgetArea>(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<Qt::DockWidgetArea>(spy.at(0).at(0)),
Qt::TopDockWidgetArea);
QCOMPARE(dockLocation(&spy), Qt::TopDockWidgetArea);
}
void tst_QDockWidget::setTitleBarWidget()