QDockWidget: Fix group unplugging
A floating dock widget could either be a single QDockWidget object, or a QDockWidgetGroupWindow with a single QDockWidget child. The former could be dropped on the latter. Dropping the latter on the former caused a crash. The existence of QDockWidgetGroupWindows with a single dock widget child was accepted to be by design. Previous fixes, such as9ff40b59da
, attempted to wrap all single floating dock widgets in QDockWidgetGroupWindows. These attempts fell short, because of the manifold programmatic and manual options to create a floating dock widget: - drag a single dock widget out of a main window dock area - drag a dock widget out of a tab bar on the main window - drag a dock widget out of a floating tab - call `QDockWidget::setFloating(true)` in any situation - create a new QDockWidget, that floats from the beginning Whenever a QDockWidgetGroupWindow with a single QDockWidget child was hovered and/or dropped on a QDockWidget without a group window, crashes or screen artifacts were observed. Previous fixes made them occur less often. QDockWidgetGroupWindow is not designed to hold a single QDockWidget child. Such a state is inconsistent and may only exist, while a QDockWidgetGroupWindow is constructed. The reason why such invalid QDockWidgetGroupWindows started to exist, is a bool trap: QDockWidgetPrivate::mouseMoveEvent() starts a drag operation, when a dock widget is moved by mouse. It called startDrag() with no argument, which defaulted to startDrag(true) and caused a group drag. This assumption is *correct*, when a tabbed group of dock widgets is dragged out of the main dock as a whole, to become floating tabs. *wrong*, when a single dock widget is dragged out of a docked group, to become a single floating dock widget. In the second case, the dock widget was wrapped in a new, floating, invisible QDockWidgetGroupWindow. Looking like a single, floating dock widget, the group window caused a crash, when attempted to be dropped on another dock widget. This patch eliminates all cases, where a QDockWidgetGroupWindow with a single QDockWidget is created: (1) Implement QDockWidgetPrivate::isTabbed(). This enables mouseMoveEvent to determine, whether the move relates to a group of tabbed dock widgets, or to a single dock widget. startDrag() can therefore be called with the right argument. It will no longer create a QDockWidgetGroupWindow with a single QDockWidget child. (2) Change QMainWindowTabBar::mouseReleaseEvent When a dock widget was dragged out of a tab bar and became a single, floating dock widget, it was still parented to the group window. That is wrong, because it has no more relationship with that group window. => Reparent it to the main window, just like any other single floating dock widget. That enables QDockWidgetGroupWindow to detect, that the 2nd last child has gone and no more group window is needed (see next point). (3) React to reparenting, closing and deleting If the second last dock widget in a floating tab gets closed (manually or programmatically), reparented or deleted, also unplug the last one and remove the group window. (4) Amend9ff40b59da
Remove the code path where a QDockWidgetGroupWindow with a single QDockWidget child was created 'just in case', to make it compatible others, created by (1), (2) or (3). (5) Change QMainWindowLayout::hover() When the hover ends without a successful drop and a temporary group window with a single dock widget child has been created, remove the group window. The patch fixes smaller inconsistencies, which have not become visible due to assertions and crashes earlier in the chain. The patch finally extends tst_QDockWidget, to cover all 4 cases. - Creation of floating tabs The creation of floating tabs is extracted from floatingTabs() to the helper function createFloatingTabs(). In addition to creating floating tabs, the helper verifies that dragging a dock widget out of the main window doesn't accidently wrap it in a group window. This covers case (1). - tst_QDockWidget::floatingTabs() The test function verifies now, that both test dock widgets have the same path before plugging them together and after unplugging them from the floating tab. This covers case(4). - tst_QDockwidget::deleteFloatingTabWithSingleDockWidget() This test function is added, to cover cases (2) and (3). - tst_QDockWidget::hoverWithoutDrop() This test function hovers two floating dock widgets hover each other, and returns the moved dock widget to its origin before releasing the mouse. This covers case(5). This fixes a lot of long standing bugs, making the author of this patch modestly happy :-) Fixes: QTBUG-118223 Fixes: QTBUG-99136 Fixes: QTBUG-118578 Fixes: QTBUG-118579 Fixes: QTBUG-56799 Fixes: QTBUG-35736 Fixes: QTBUG-63448 Fixes: QTBUG-88329 Fixes: QTBUG-88157 Fixes: QTBUG-94097 Fixes: QTBUG-44540 Fixes: QTBUG-53808 Fixes: QTBUG-72915 Fixes: QTBUG-53438 Found-by: Keith Kyzivat <keith.kyzivat@qt.io> Found-by: Frederic Lefebvre <frederic.lefebvre@qt.io> Pick-to: 6.6 6.5 Change-Id: I51b5f9e40cb2dbe55fb14d769541067730538463 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
0b10b7476c
commit
e6d85cf28b
@ -847,6 +847,11 @@ void QDockWidgetPrivate::endDrag(EndDragMode mode)
|
||||
tabPosition = mwLayout->tabPosition(toDockWidgetArea(dwgw->layoutInfo()->dockPos));
|
||||
}
|
||||
#endif
|
||||
// Reparent, if the drag was out of a dock widget group window
|
||||
if (mode == EndDragMode::LocationChange) {
|
||||
if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(q->parentWidget()))
|
||||
groupWindow->reparent(q);
|
||||
}
|
||||
}
|
||||
q->activateWindow();
|
||||
} else {
|
||||
@ -948,6 +953,15 @@ bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QDockWidgetPrivate::isTabbed() const
|
||||
{
|
||||
Q_Q(const QDockWidget);
|
||||
QDockWidget *that = const_cast<QDockWidget *>(q);
|
||||
auto *mwLayout = qt_mainwindow_layout_from_dock(that);
|
||||
Q_ASSERT(mwLayout);
|
||||
return mwLayout->isDockWidgetTabbed(q);
|
||||
}
|
||||
|
||||
bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
bool ret = false;
|
||||
@ -978,7 +992,8 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
startDrag(DragScope::Group);
|
||||
const DragScope scope = isTabbed() ? DragScope::Group : DragScope::Widget;
|
||||
startDrag(scope);
|
||||
q->grabMouse();
|
||||
ret = true;
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ public:
|
||||
void setResizerActive(bool active);
|
||||
|
||||
bool isAnimating() const;
|
||||
bool isTabbed() const;
|
||||
|
||||
private:
|
||||
QWidgetResizeHandler *resizer = nullptr;
|
||||
|
@ -151,6 +151,21 @@ QDebug operator<<(QDebug debug, const QMainWindowLayout *layout)
|
||||
return debug;
|
||||
}
|
||||
|
||||
// Use this to dump item lists of all populated main window docks.
|
||||
// Use DUMP macro inside QMainWindowLayout
|
||||
#if 0
|
||||
static void dumpItemLists(const QMainWindowLayout *layout, const char *function, const char *comment)
|
||||
{
|
||||
for (int i = 0; i < QInternal::DockCount; ++i) {
|
||||
const auto &list = layout->layoutState.dockAreaLayout.docks[i].item_list;
|
||||
if (list.isEmpty())
|
||||
continue;
|
||||
qDebug() << function << comment << "Dock" << i << list;
|
||||
}
|
||||
}
|
||||
#define DUMP(comment) dumpItemLists(this, __FUNCTION__, comment)
|
||||
#endif // 0
|
||||
|
||||
#endif // QT_CONFIG(dockwidget) && !defined(QT_NO_DEBUG)
|
||||
|
||||
/******************************************************************************
|
||||
@ -403,8 +418,8 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
|
||||
}
|
||||
|
||||
// Make sure to reparent the possibly floating or hidden QDockWidgets to the parent
|
||||
const auto dockWidgets = findChildren<QDockWidget *>(Qt::FindDirectChildrenOnly);
|
||||
for (QDockWidget *dw : dockWidgets) {
|
||||
const auto dockWidgetsList = dockWidgets();
|
||||
for (QDockWidget *dw : dockWidgetsList) {
|
||||
const bool wasFloating = dw->isFloating();
|
||||
const bool wasHidden = dw->isHidden();
|
||||
dw->setParent(parentWidget());
|
||||
@ -607,6 +622,108 @@ void QDockWidgetGroupWindow::apply()
|
||||
layoutInfo()->apply(false);
|
||||
}
|
||||
|
||||
void QDockWidgetGroupWindow::childEvent(QChildEvent *event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case QEvent::ChildRemoved:
|
||||
if (auto *dockWidget = qobject_cast<QDockWidget *>(event->child()))
|
||||
dockWidget->removeEventFilter(this);
|
||||
destroyIfSingleItemLeft();
|
||||
break;
|
||||
case QEvent::ChildAdded:
|
||||
if (auto *dockWidget = qobject_cast<QDockWidget *>(event->child()))
|
||||
dockWidget->installEventFilter(this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool QDockWidgetGroupWindow::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
auto *dockWidget = qobject_cast<QDockWidget *>(obj);
|
||||
if (!dockWidget)
|
||||
return QWidget::eventFilter(obj, event);
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::Close:
|
||||
// We don't want closed dock widgets in a floating tab
|
||||
// => dock it to the main dock, before closing;
|
||||
reparent(dockWidget);
|
||||
dockWidget->setFloating(false);
|
||||
break;
|
||||
|
||||
case QEvent::Hide:
|
||||
// if the dock widget is not an active tab, it is hidden anyway.
|
||||
// if it is the active tab, hide the whole group.
|
||||
if (dockWidget->isVisible())
|
||||
hide();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QWidget::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void QDockWidgetGroupWindow::destroyIfSingleItemLeft()
|
||||
{
|
||||
const auto &dockWidgets = this->dockWidgets();
|
||||
|
||||
// Handle only the last dock
|
||||
if (dockWidgets.count() != 1)
|
||||
return;
|
||||
|
||||
auto *lastDockWidget = dockWidgets.at(0);
|
||||
|
||||
// If the last remaining dock widget is not in the group window's item_list,
|
||||
// a group window is being docked on a main window docking area.
|
||||
// => don't interfere
|
||||
if (layoutInfo()->indexOf(lastDockWidget).isEmpty())
|
||||
return;
|
||||
|
||||
auto *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
|
||||
QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
|
||||
|
||||
// Unplug the last remaining dock widget and hide the group window, to avoid flickering
|
||||
mwLayout->unplug(lastDockWidget, QDockWidgetPrivate::DragScope::Widget);
|
||||
lastDockWidget->setGeometry(geometry());
|
||||
hide();
|
||||
|
||||
// Get the layout info for the main window dock, where dock widgets need to go
|
||||
QDockAreaLayoutInfo &parentInfo = mwLayout->layoutState.dockAreaLayout.docks[layoutInfo()->dockPos];
|
||||
|
||||
// Re-parent last dock widget
|
||||
reparent(lastDockWidget);
|
||||
|
||||
// the group window could still have placeholder items => clear everything
|
||||
layoutInfo()->item_list.clear();
|
||||
|
||||
// remove the group window and the dock's item_list pointing to it.
|
||||
parentInfo.remove(this);
|
||||
destroyOrHideIfEmpty();
|
||||
}
|
||||
|
||||
void QDockWidgetGroupWindow::reparent(QDockWidget *dockWidget)
|
||||
{
|
||||
// reparent a dockWidget to the main window
|
||||
// - remove it from the floating dock's layout info
|
||||
// - insert it to the main dock's layout info
|
||||
// Finally, set draggingDock to nullptr, since the drag is finished.
|
||||
auto *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
|
||||
Q_ASSERT(mainWindow);
|
||||
QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
|
||||
Q_ASSERT(mwLayout);
|
||||
QDockAreaLayoutInfo &parentInfo = mwLayout->layoutState.dockAreaLayout.docks[layoutInfo()->dockPos];
|
||||
dockWidget->removeEventFilter(this);
|
||||
parentInfo.add(dockWidget);
|
||||
layoutInfo()->remove(dockWidget);
|
||||
const bool wasFloating = dockWidget->isFloating();
|
||||
const bool wasVisible = dockWidget->isVisible();
|
||||
dockWidget->setParent(mainWindow);
|
||||
dockWidget->setFloating(wasFloating);
|
||||
dockWidget->setVisible(wasVisible);
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
@ -1745,11 +1862,14 @@ void QMainWindowLayout::keepSize(QDockWidget *w)
|
||||
// Handle custom tooltip, and allow to drag tabs away.
|
||||
class QMainWindowTabBar : public QTabBar
|
||||
{
|
||||
Q_OBJECT
|
||||
QMainWindow *mainWindow;
|
||||
QPointer<QDockWidget> draggingDock; // Currently dragging (detached) dock widget
|
||||
~QMainWindowTabBar();
|
||||
public:
|
||||
QMainWindowTabBar(QMainWindow *parent);
|
||||
QDockWidget *dockAt(int index) const;
|
||||
QList<QDockWidget *> dockWidgets() const;
|
||||
protected:
|
||||
bool event(QEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent*) override;
|
||||
@ -1763,6 +1883,29 @@ QMainWindowTabBar::QMainWindowTabBar(QMainWindow *parent)
|
||||
setExpanding(false);
|
||||
}
|
||||
|
||||
QList<QDockWidget *> QMainWindowTabBar::dockWidgets() const
|
||||
{
|
||||
QList<QDockWidget *> docks;
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
if (QDockWidget *dock = dockAt(i))
|
||||
docks << dock;
|
||||
}
|
||||
return docks;
|
||||
}
|
||||
|
||||
QDockWidget *QMainWindowTabBar::dockAt(int index) const
|
||||
{
|
||||
QMainWindowTabBar *that = const_cast<QMainWindowTabBar *>(this);
|
||||
QMainWindowLayout* mlayout = qt_mainwindow_layout(mainWindow);
|
||||
QDockAreaLayoutInfo *info = mlayout->dockInfo(that);
|
||||
if (!info)
|
||||
return nullptr;
|
||||
const int itemIndex = info->tabIndexToListIndex(index);
|
||||
Q_ASSERT(itemIndex >= 0 && itemIndex < info->item_list.count());
|
||||
const QDockAreaLayoutItem &item = info->item_list.at(itemIndex);
|
||||
return item.widgetItem ? qobject_cast<QDockWidget *>(item.widgetItem->widget()) : nullptr;
|
||||
}
|
||||
|
||||
void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
|
||||
{
|
||||
// The QTabBar handles the moving (reordering) of tabs.
|
||||
@ -1776,13 +1919,8 @@ void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
|
||||
offset *= 3;
|
||||
QRect r = rect().adjusted(-offset, -offset, offset, offset);
|
||||
if (d->dragInProgress && !r.contains(e->position().toPoint()) && d->validIndex(d->pressedIndex)) {
|
||||
QMainWindowLayout* mlayout = qt_mainwindow_layout(mainWindow);
|
||||
QDockAreaLayoutInfo *info = mlayout->dockInfo(this);
|
||||
Q_ASSERT(info);
|
||||
int idx = info->tabIndexToListIndex(d->pressedIndex);
|
||||
const QDockAreaLayoutItem &item = info->item_list.at(idx);
|
||||
if (item.widgetItem
|
||||
&& (draggingDock = qobject_cast<QDockWidget *>(item.widgetItem->widget()))) {
|
||||
draggingDock = dockAt(d->pressedIndex);
|
||||
if (draggingDock) {
|
||||
// We should drag this QDockWidget away by unpluging it.
|
||||
// First cancel the QTabBar's internal move
|
||||
d->moveTabFinished(d->pressedIndex);
|
||||
@ -1858,6 +1996,23 @@ bool QMainWindowTabBar::event(QEvent *e)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QMainWindowLayout::isDockWidgetTabbed(const QDockWidget *dockWidget) const
|
||||
{
|
||||
for (auto *bar : std::as_const(usedTabBars)) {
|
||||
// A single dock widget in a tab bar is not considered to be tabbed.
|
||||
// This is to make sure, we don't drag an empty QDockWidgetGroupWindow around.
|
||||
// => only consider tab bars with two or more tabs.
|
||||
if (bar->count() <= 1)
|
||||
continue;
|
||||
auto *tabBar = qobject_cast<QMainWindowTabBar *>(bar);
|
||||
Q_ASSERT(tabBar);
|
||||
const auto dockWidgets = tabBar->dockWidgets();
|
||||
if (std::find(dockWidgets.begin(), dockWidgets.end(), dockWidget) != dockWidgets.end())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QTabBar *QMainWindowLayout::getTabBar()
|
||||
{
|
||||
if (!usedTabBars.isEmpty() && !isInRestoreState) {
|
||||
@ -2596,73 +2751,10 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, QDockWidgetPrivate::Drag
|
||||
// We are unplugging a single dock widget from a floating window.
|
||||
QDockWidget *dockWidget = qobject_cast<QDockWidget *>(widget);
|
||||
Q_ASSERT(dockWidget); // cannot be a QDockWidgetGroupWindow because it's not floating.
|
||||
|
||||
// unplug the widget first
|
||||
dockWidget->d_func()->unplug(widget->geometry());
|
||||
|
||||
// Create a floating tab, copy properties and generate layout info
|
||||
QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow();
|
||||
const QInternal::DockPosition dockPos = groupWindow->layoutInfo()->dockPos;
|
||||
QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
|
||||
|
||||
const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(dockWidget);
|
||||
|
||||
// Populate newly created DockAreaLayoutInfo of floating tabs
|
||||
*info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, dockPos,
|
||||
Qt::Horizontal, shape,
|
||||
layoutState.mainWindow);
|
||||
|
||||
// Create tab and hide it as group window contains only one widget
|
||||
info->tabbed = true;
|
||||
info->tabBar = getTabBar();
|
||||
info->tabBar->hide();
|
||||
updateGapIndicator();
|
||||
|
||||
// Reparent it to a QDockWidgetGroupLayout
|
||||
floatingTabs->setGeometry(dockWidget->geometry());
|
||||
|
||||
// Append reference to floatingTabs to the dock's item_list
|
||||
parentItem.widgetItem = new QDockWidgetGroupWindowItem(floatingTabs);
|
||||
layoutState.dockAreaLayout.docks[dockPos].item_list.append(parentItem);
|
||||
|
||||
// use populated parentItem to set reference to dockWidget as the first item in own list
|
||||
parentItem.widgetItem = new QDockWidgetItem(dockWidget);
|
||||
info->item_list = {parentItem};
|
||||
|
||||
// Add non-gap items of the dock to the tab bar
|
||||
for (const auto &listItem : layoutState.dockAreaLayout.docks[dockPos].item_list) {
|
||||
if (listItem.GapItem || !listItem.widgetItem)
|
||||
continue;
|
||||
info->tabBar->addTab(listItem.widgetItem->widget()->objectName());
|
||||
}
|
||||
|
||||
// Re-parent and fit
|
||||
floatingTabs->setParent(layoutState.mainWindow);
|
||||
floatingTabs->layoutInfo()->fitItems();
|
||||
floatingTabs->layoutInfo()->apply(dockOptions & QMainWindow::AnimatedDocks);
|
||||
groupWindow->layoutInfo()->fitItems();
|
||||
groupWindow->layoutInfo()->apply(dockOptions & QMainWindow::AnimatedDocks);
|
||||
dockWidget->d_func()->tabPosition = layoutState.mainWindow->tabPosition(toDockWidgetArea(dockPos));
|
||||
info->reparentWidgets(floatingTabs);
|
||||
dockWidget->setParent(floatingTabs);
|
||||
info->updateTabBar();
|
||||
|
||||
// Show the new item
|
||||
const QList<int> path = layoutState.indexOf(floatingTabs);
|
||||
QRect r = layoutState.itemRect(path);
|
||||
savedState = layoutState;
|
||||
savedState.fitLayout();
|
||||
|
||||
// Update gap, fix orientation, raise and show
|
||||
currentGapPos = path;
|
||||
currentGapRect = r;
|
||||
updateGapIndicator();
|
||||
fixToolBarOrientation(parentItem.widgetItem, currentGapPos.at(1));
|
||||
floatingTabs->show();
|
||||
floatingTabs->raise();
|
||||
|
||||
qCDebug(lcQpaDockWidgets) << "Unplugged from floating dock:" << widget << "from" << parentItem.widgetItem;
|
||||
return parentItem.widgetItem;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2815,7 +2907,7 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
|
||||
continue;
|
||||
|
||||
// Check permission to dock on another dock widget or floating dock
|
||||
// FIXME in 6.4
|
||||
// FIXME in Qt 7
|
||||
|
||||
if (w != widget && w->isWindow() && w->isVisible() && !w->isMinimized())
|
||||
candidates << w;
|
||||
@ -2853,16 +2945,26 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
|
||||
floatingTabs->setGeometry(dropTo->geometry());
|
||||
QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
|
||||
const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(dropTo);
|
||||
const QInternal::DockPosition dockPosition = toDockPos(dockWidgetArea(dropTo));
|
||||
|
||||
// dropTo and widget may be in a state where they transition
|
||||
// from being a group window child to a single floating dock widget.
|
||||
// In that case, their path to a main window dock may not have been
|
||||
// updated yet.
|
||||
// => ask both and fall back to dock 1 (right dock)
|
||||
QInternal::DockPosition dockPosition = toDockPos(dockWidgetArea(dropTo));
|
||||
if (dockPosition == QInternal::DockPosition::DockCount)
|
||||
dockPosition = toDockPos(dockWidgetArea(widget));
|
||||
if (dockPosition == QInternal::DockPosition::DockCount)
|
||||
dockPosition = QInternal::DockPosition::RightDock;
|
||||
|
||||
*info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, dockPosition,
|
||||
Qt::Horizontal, shape,
|
||||
static_cast<QMainWindow *>(parentWidget()));
|
||||
info->tabBar = getTabBar();
|
||||
info->tabbed = true;
|
||||
QLayout *parentLayout = dropTo->parentWidget()->layout();
|
||||
info->item_list.append(
|
||||
QDockAreaLayoutItem(parentLayout->takeAt(parentLayout->indexOf(dropTo))));
|
||||
|
||||
info->add(dropTo);
|
||||
QDockAreaLayoutInfo &parentInfo = layoutState.dockAreaLayout.docks[dockPosition];
|
||||
parentInfo.add(floatingTabs);
|
||||
dropTo->setParent(floatingTabs);
|
||||
qCDebug(lcQpaDockWidgets) << "Wrapping" << widget << "into floating tabs" << floatingTabs;
|
||||
w = floatingTabs;
|
||||
@ -2875,15 +2977,21 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
|
||||
qCDebug(lcQpaDockWidgets) << "Raising" << widget;
|
||||
}
|
||||
#endif
|
||||
auto group = qobject_cast<QDockWidgetGroupWindow *>(w);
|
||||
Q_ASSERT(group);
|
||||
if (group->hover(hoverTarget, group->mapFromGlobal(mousePos))) {
|
||||
setCurrentHoveredFloat(group);
|
||||
auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(w);
|
||||
Q_ASSERT(groupWindow);
|
||||
if (groupWindow->hover(hoverTarget, groupWindow->mapFromGlobal(mousePos))) {
|
||||
setCurrentHoveredFloat(groupWindow);
|
||||
applyState(layoutState); // update the tabbars
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If a temporary group window has been created during a hover,
|
||||
// remove it, if it has only one dockwidget child
|
||||
if (currentHoveredFloat)
|
||||
currentHoveredFloat->destroyIfSingleItemLeft();
|
||||
|
||||
setCurrentHoveredFloat(nullptr);
|
||||
layoutState.dockAreaLayout.fallbackToSizeHints = false;
|
||||
#endif // QT_CONFIG(dockwidget)
|
||||
@ -3091,4 +3199,5 @@ Qt::DropAction QMainWindowLayout::performPlatformWidgetDrag(QLayoutItem *widgetI
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "qmainwindowlayout.moc"
|
||||
#include "moc_qmainwindowlayout_p.cpp"
|
||||
|
@ -333,6 +333,9 @@ public:
|
||||
void updateCurrentGapRect();
|
||||
void restore();
|
||||
void apply();
|
||||
void childEvent(QChildEvent *event) override;
|
||||
void reparent(QDockWidget *dockWidget);
|
||||
void destroyIfSingleItemLeft();
|
||||
QList<QDockWidget *> dockWidgets() const { return findChildren<QDockWidget *>(); }
|
||||
|
||||
QRect currentGapRect;
|
||||
@ -343,6 +346,7 @@ signals:
|
||||
|
||||
protected:
|
||||
bool event(QEvent *) override;
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
@ -574,6 +578,7 @@ public:
|
||||
#if QT_CONFIG(dockwidget)
|
||||
QPointer<QDockWidgetGroupWindow> currentHoveredFloat; // set when dragging over a floating dock widget
|
||||
void setCurrentHoveredFloat(QDockWidgetGroupWindow *w);
|
||||
bool isDockWidgetTabbed(const QDockWidget *dockWidget) const;
|
||||
#endif
|
||||
bool isInApplyState = false;
|
||||
|
||||
|
@ -24,3 +24,13 @@ android
|
||||
qnx
|
||||
macos
|
||||
b2qt
|
||||
|
||||
# OSes are flaky because of unplugging and plugging requires
|
||||
# precise calculation of the title bar area for mouse emulation
|
||||
# That's not possible for floating dock widgets.
|
||||
[deleteFloatingTabWithSingleDockWidget]
|
||||
qnx
|
||||
b2qt
|
||||
arm
|
||||
android
|
||||
macos
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "private/qmainwindowlayout_p.h"
|
||||
#include <QAbstractButton>
|
||||
#include <qlineedit.h>
|
||||
#include <QtGui/qpa/qplatformwindow.h>
|
||||
#include <qtabbar.h>
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
@ -67,6 +68,11 @@ private slots:
|
||||
|
||||
// test floating tabs, item_tree and window title consistency
|
||||
void floatingTabs();
|
||||
void hoverWithoutDrop();
|
||||
|
||||
// floating tab gets removed, when last child goes away
|
||||
void deleteFloatingTabWithSingleDockWidget_data();
|
||||
void deleteFloatingTabWithSingleDockWidget();
|
||||
|
||||
// test hide & show
|
||||
void hideAndShow();
|
||||
@ -81,9 +87,15 @@ private slots:
|
||||
private:
|
||||
// helpers and consts for dockPermissions, hideAndShow, closeAndDelete
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢, QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const;
|
||||
void createTestWidgets(QMainWindow* &MainWindow, QPointer<QWidget> ¢,
|
||||
QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2) const;
|
||||
|
||||
void unplugAndResize(QMainWindow* MainWindow, QDockWidget* dw, QPoint home, QSize size) const;
|
||||
|
||||
void createFloatingTabs(QMainWindow* &MainWindow, QPointer<QWidget> ¢,
|
||||
QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2,
|
||||
QList<int> &path1, QList<int> &path2) const;
|
||||
|
||||
static inline QPoint dragPoint(QDockWidget* dockWidget);
|
||||
static inline QPoint home1(QMainWindow* MainWindow)
|
||||
{ return MainWindow->mapToGlobal(MainWindow->rect().topLeft() + QPoint(0.1 * MainWindow->width(), 0.1 * MainWindow->height())); }
|
||||
@ -103,13 +115,26 @@ private:
|
||||
bool checkFloatingTabs(QMainWindow* MainWindow, QPointer<QDockWidgetGroupWindow> &ftabs, const QList<QDockWidget*> &dwList = {}) const;
|
||||
|
||||
// move a dock widget
|
||||
void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from = QPoint()) const;
|
||||
enum class MoveDockWidgetRule {
|
||||
Drop,
|
||||
Abort
|
||||
};
|
||||
|
||||
void moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const;
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
// Message handling for xcb error QTBUG 82059
|
||||
static void xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||
|
||||
enum class ChildRemovalReason {
|
||||
Destroyed,
|
||||
Closed,
|
||||
Reparented
|
||||
};
|
||||
|
||||
public:
|
||||
bool xcbError = false;
|
||||
bool platformSupportingRaise = true;
|
||||
#endif
|
||||
private:
|
||||
|
||||
@ -1185,7 +1210,7 @@ QPoint tst_QDockWidget::dragPoint(QDockWidget* dockWidget)
|
||||
return dockWidget->mapToGlobal(dwlayout->titleArea().center());
|
||||
}
|
||||
|
||||
void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) const
|
||||
void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from, MoveDockWidgetRule rule) const
|
||||
{
|
||||
Q_ASSERT(dw);
|
||||
|
||||
@ -1202,12 +1227,22 @@ void tst_QDockWidget::moveDockWidget(QDockWidget* dw, QPoint to, QPoint from) co
|
||||
QTest::mouseMove(dw, target);
|
||||
qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << target;
|
||||
qCDebug(lcTestDockWidget) << "Move" << dw->objectName() << "to" << to;
|
||||
QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target);
|
||||
QTest::qWait(waitingTime);
|
||||
if (rule == MoveDockWidgetRule::Drop) {
|
||||
QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), target);
|
||||
QTest::qWait(waitingTime);
|
||||
|
||||
// Verify WindowActive only for floating dock widgets
|
||||
if (dw->isFloating())
|
||||
QTRY_VERIFY(QTest::qWaitForWindowActive(dw));
|
||||
// Verify WindowActive only for floating dock widgets
|
||||
if (dw->isFloating())
|
||||
QTRY_VERIFY(QTest::qWaitForWindowActive(dw));
|
||||
return;
|
||||
}
|
||||
qCDebug(lcTestDockWidget) << "Aborting move and dropping at origin";
|
||||
|
||||
// Give animations some time
|
||||
QTest::qWait(waitingTime);
|
||||
QTest::mouseMove(dw, from);
|
||||
QTest::mouseRelease(dw, Qt::LeftButton, Qt::KeyboardModifiers(), from);
|
||||
QTest::qWait(waitingTime);
|
||||
}
|
||||
|
||||
void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw, QPoint home, QSize size) const
|
||||
@ -1255,7 +1290,7 @@ void tst_QDockWidget::unplugAndResize(QMainWindow* mainWindow, QDockWidget* dw,
|
||||
QPoint pos1 = dw->mapToGlobal(dw->rect().center());
|
||||
pos1.rx() += mx;
|
||||
pos1.ry() += my;
|
||||
moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()));
|
||||
moveDockWidget(dw, pos1, dw->mapToGlobal(dw->rect().center()), MoveDockWidgetRule::Drop);
|
||||
QTRY_VERIFY(dw->isFloating());
|
||||
|
||||
// Unplugged object's size may differ max. by 2x frame size
|
||||
@ -1317,6 +1352,71 @@ bool tst_QDockWidget::checkFloatingTabs(QMainWindow* mainWindow, QPointer<QDockW
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
// Statics for xcb error, raise() suppert / msg handler
|
||||
static tst_QDockWidget *qThis = nullptr;
|
||||
static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
|
||||
#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0)
|
||||
|
||||
// detect xcb error and missing raise() support
|
||||
// qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window
|
||||
void tst_QDockWidget::xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
Q_ASSERT(oldMessageHandler);
|
||||
|
||||
if (type == QtWarningMsg) {
|
||||
Q_ASSERT(qThis);
|
||||
if (QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error"))
|
||||
qThis->xcbError = true;
|
||||
if (msg.contains("does not support raise"))
|
||||
qThis->platformSupportingRaise = false;
|
||||
}
|
||||
|
||||
return oldMessageHandler(type, context, msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QDockWidget::createFloatingTabs(QMainWindow* &mainWindow, QPointer<QWidget> ¢,
|
||||
QPointer<QDockWidget> &d1, QPointer<QDockWidget> &d2,
|
||||
QList<int> &path1, QList<int> &path2) const
|
||||
{
|
||||
createTestWidgets(mainWindow, cent, d1, d2);
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
qThis = const_cast<tst_QDockWidget *>(this);
|
||||
oldMessageHandler = qInstallMessageHandler(xcbMessageHandler);
|
||||
auto resetMessageHandler = qScopeGuard([] { qInstallMessageHandler(oldMessageHandler); });
|
||||
#endif
|
||||
|
||||
// Test will fail if platform doesn't support raise.
|
||||
mainWindow->windowHandle()->handle()->raise();
|
||||
if (!platformSupportingRaise)
|
||||
QSKIP("Platform not supporting raise(). Floating tab based tests will fail.");
|
||||
|
||||
// remember paths to d1 and d2
|
||||
QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout());
|
||||
path1 = layout->layoutState.indexOf(d1);
|
||||
path2 = layout->layoutState.indexOf(d2);
|
||||
|
||||
// unplug and resize both dock widgets
|
||||
unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow));
|
||||
unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow));
|
||||
|
||||
// docks must be parented to the main window, no group window must exist
|
||||
QCOMPARE(d1->parentWidget(), mainWindow);
|
||||
QCOMPARE(d2->parentWidget(), mainWindow);
|
||||
QVERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty());
|
||||
|
||||
// Test plugging
|
||||
qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***";
|
||||
qCDebug(lcTestDockWidget) << "**********(test plugging)*************";
|
||||
qCDebug(lcTestDockWidget) << "Move d1 over d2";
|
||||
moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop);
|
||||
|
||||
// Now MainWindow has to have a floatingTab child
|
||||
QPointer<QDockWidgetGroupWindow> ftabs;
|
||||
QTRY_VERIFY(checkFloatingTabs(mainWindow, ftabs, QList<QDockWidget *>() << d1 << d2));
|
||||
}
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
|
||||
// test floating tabs and item_tree consistency
|
||||
@ -1333,7 +1433,9 @@ void tst_QDockWidget::floatingTabs()
|
||||
QPointer<QDockWidget> d2;
|
||||
QPointer<QWidget> cent;
|
||||
QMainWindow* mainWindow;
|
||||
createTestWidgets(mainWindow, cent, d1, d2);
|
||||
QList<int> path1;
|
||||
QList<int> path2;
|
||||
createFloatingTabs(mainWindow, cent, d1, d2, path1, path2);
|
||||
std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
|
||||
|
||||
/*
|
||||
@ -1341,22 +1443,6 @@ void tst_QDockWidget::floatingTabs()
|
||||
* expected behavior: QDOckWidgetGroupWindow with both widgets is created
|
||||
*/
|
||||
|
||||
// remember paths to d1 and d2
|
||||
QMainWindowLayout* layout = qobject_cast<QMainWindowLayout*>(mainWindow->layout());
|
||||
const QList<int> path1 = layout->layoutState.indexOf(d1);
|
||||
const QList<int> path2 = layout->layoutState.indexOf(d2);
|
||||
|
||||
// unplug and resize both dock widgets
|
||||
unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow));
|
||||
unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow));
|
||||
|
||||
// Test plugging
|
||||
qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***";
|
||||
qCDebug(lcTestDockWidget) << "**********(test plugging)*************";
|
||||
qCDebug(lcTestDockWidget) << "Move d1 over d2";
|
||||
moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()));
|
||||
|
||||
// Both dock widgets must no longer be floating
|
||||
// disabled due to flakiness on macOS and Windows
|
||||
if (d1->isFloating())
|
||||
qWarning("OS flakiness: D1 is docked and reports being floating");
|
||||
@ -1407,10 +1493,9 @@ void tst_QDockWidget::floatingTabs()
|
||||
// Plug back into dock areas
|
||||
qCDebug(lcTestDockWidget) << "*** test plugging back to dock areas ***";
|
||||
qCDebug(lcTestDockWidget) << "Move d1 to left dock";
|
||||
//moveDockWidget(d1, d1->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea)));
|
||||
moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea));
|
||||
moveDockWidget(d1, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
|
||||
qCDebug(lcTestDockWidget) << "Move d2 to right dock";
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea));
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::RightDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
|
||||
|
||||
qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before plugging back.";
|
||||
QTest::qWait(waitBeforeClose);
|
||||
@ -1424,38 +1509,99 @@ void tst_QDockWidget::floatingTabs()
|
||||
QTRY_VERIFY(ftabs.isNull());
|
||||
|
||||
// Check if paths are consistent
|
||||
QMainWindowLayout* layout = qobject_cast<QMainWindowLayout *>(mainWindow->layout());
|
||||
qCDebug(lcTestDockWidget) << "Checking path consistency" << layout->layoutState.indexOf(d1) << layout->layoutState.indexOf(d2);
|
||||
|
||||
// Path1 must be identical
|
||||
QTRY_COMPARE(path1, layout->layoutState.indexOf(d1));
|
||||
|
||||
// d1 must have a gap item due to size change
|
||||
QTRY_COMPARE(layout->layoutState.indexOf(d2), QList<int>() << path2 << 0);
|
||||
// Paths must be identical
|
||||
QTRY_COMPARE(layout->layoutState.indexOf(d1), path1);
|
||||
QTRY_COMPARE(layout->layoutState.indexOf(d2), path2);
|
||||
#else
|
||||
QSKIP("test requires -developer-build option");
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
}
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
// Statics for xcb error / msg handler
|
||||
static tst_QDockWidget *qThis = nullptr;
|
||||
static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext&, const QString&);
|
||||
#define QXCBVERIFY(cond) do { if (xcbError) QSKIP("Test skipped due to XCB error"); QVERIFY(cond); } while (0)
|
||||
|
||||
// detect xcb error
|
||||
// qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window
|
||||
void tst_QDockWidget::xcbMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget_data()
|
||||
{
|
||||
Q_ASSERT(oldMessageHandler);
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
QTest::addColumn<int>("reason");
|
||||
QTest::addRow("Delete child") << static_cast<int>(ChildRemovalReason::Destroyed);
|
||||
QTest::addRow("Close child") << static_cast<int>(ChildRemovalReason::Closed);
|
||||
QTest::addRow("Reparent child") << static_cast<int>(ChildRemovalReason::Reparented);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (type == QtWarningMsg && QString(context.category) == "qt.qpa.xcb" && msg.contains("internal error")) {
|
||||
Q_ASSERT(qThis);
|
||||
qThis->xcbError = true;
|
||||
void tst_QDockWidget::deleteFloatingTabWithSingleDockWidget()
|
||||
{
|
||||
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
||||
QSKIP("Test skipped on Wayland.");
|
||||
#ifdef Q_OS_WIN
|
||||
QSKIP("Test skipped on Windows platforms");
|
||||
#endif // Q_OS_WIN
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
|
||||
QFETCH(int, reason);
|
||||
const ChildRemovalReason removalReason = static_cast<ChildRemovalReason>(reason);
|
||||
|
||||
QPointer<QDockWidget> d1;
|
||||
QPointer<QDockWidget> d2;
|
||||
QPointer<QWidget> cent;
|
||||
QMainWindow* mainWindow;
|
||||
QList<int> path1;
|
||||
QList<int> path2;
|
||||
createFloatingTabs(mainWindow, cent, d1, d2, path1, path2);
|
||||
std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
|
||||
|
||||
switch (removalReason) {
|
||||
case ChildRemovalReason::Destroyed:
|
||||
delete d1;
|
||||
break;
|
||||
case ChildRemovalReason::Closed:
|
||||
d1->close();
|
||||
break;
|
||||
case ChildRemovalReason::Reparented:
|
||||
// This will create an invalid state, because setParent() doesn't fix the item_list.
|
||||
// Testing this case anyway, because setParent() includig item_list fixup is executed,
|
||||
// when the 2nd last dock widget is dragged out of a floating tab.
|
||||
// => despite of the broken state, the group window has to be gone.
|
||||
d1->setParent(mainWindow);
|
||||
break;
|
||||
}
|
||||
|
||||
return oldMessageHandler(type, context, msg);
|
||||
}
|
||||
QTRY_VERIFY(!qobject_cast<QDockWidgetGroupWindow *>(d2->parentWidget()));
|
||||
QTRY_VERIFY(mainWindow->findChildren<QDockWidgetGroupWindow *>().isEmpty());
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
}
|
||||
|
||||
void tst_QDockWidget::hoverWithoutDrop()
|
||||
{
|
||||
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
||||
QSKIP("Test skipped on Wayland.");
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
|
||||
QPointer<QDockWidget> d1;
|
||||
QPointer<QDockWidget> d2;
|
||||
QPointer<QWidget> cent;
|
||||
QMainWindow* mainWindow;
|
||||
createTestWidgets(mainWindow, cent, d1, d2);
|
||||
std::unique_ptr<QMainWindow> up_mainWindow(mainWindow);
|
||||
|
||||
// unplug and resize both dock widgets
|
||||
unplugAndResize(mainWindow, d1, home1(mainWindow), size1(mainWindow));
|
||||
unplugAndResize(mainWindow, d2, home2(mainWindow), size2(mainWindow));
|
||||
|
||||
// Test plugging
|
||||
qCDebug(lcTestDockWidget) << "*** move d1 dock over d2 dock ***";
|
||||
qCDebug(lcTestDockWidget) << "*******(test hovering)***********";
|
||||
qCDebug(lcTestDockWidget) << "Move d1 over d2, wait and return to origin";
|
||||
const QPoint source = d1->mapToGlobal(d1->rect().center());
|
||||
const QPoint target = d2->mapToGlobal(d2->rect().center());
|
||||
moveDockWidget(d1, target, source, MoveDockWidgetRule::Abort);
|
||||
auto *groupWindow = mainWindow->findChild<QDockWidgetGroupWindow *>();
|
||||
QCOMPARE(groupWindow, nullptr);
|
||||
#else
|
||||
QSKIP("test requires -developer-build option");
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
}
|
||||
|
||||
// test hide & show
|
||||
void tst_QDockWidget::hideAndShow()
|
||||
@ -1542,7 +1688,7 @@ void tst_QDockWidget::closeAndDelete()
|
||||
|
||||
// Create a floating tab and unplug it again
|
||||
qCDebug(lcTestDockWidget) << "Move d1 over d2";
|
||||
moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()));
|
||||
moveDockWidget(d1, d2->mapToGlobal(d2->rect().center()), QPoint(), MoveDockWidgetRule::Drop);
|
||||
|
||||
// Both dock widgets must no longer be floating
|
||||
// disabled due to flakiness on macOS and Windows
|
||||
@ -1647,16 +1793,16 @@ void tst_QDockWidget::dockPermissions()
|
||||
|
||||
// Move d2 to non allowed dock areas and verify it remains floating
|
||||
qCDebug(lcTestDockWidget) << "Move d2 to top dock";
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea));
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::TopDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
|
||||
QTRY_VERIFY(d2->isFloating());
|
||||
|
||||
qCDebug(lcTestDockWidget) << "Move d2 to left dock";
|
||||
//moveDockWidget(d2, d2->mapFrom(MainWindow, dockPoint(MainWindow, Qt::LeftDockWidgetArea)));
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea));
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::LeftDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
|
||||
QTRY_VERIFY(d2->isFloating());
|
||||
|
||||
qCDebug(lcTestDockWidget) << "Move d2 to bottom dock";
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea));
|
||||
moveDockWidget(d2, dockPoint(mainWindow, Qt::BottomDockWidgetArea), QPoint(), MoveDockWidgetRule::Drop);
|
||||
QTRY_VERIFY(d2->isFloating());
|
||||
|
||||
qCDebug(lcTestDockWidget) << "Waiting" << waitBeforeClose << "ms before closing.";
|
||||
|
Loading…
Reference in New Issue
Block a user