diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 2a828e740c3..8ceaed31aac 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -82,7 +82,6 @@ using namespace QNativeInterface::Private; using namespace Qt::StringLiterals; Q_LOGGING_CATEGORY(lcWidgetPainting, "qt.widgets.painting", QtWarningMsg); -Q_LOGGING_CATEGORY(lcWidgetWindow, "qt.widgets.window", QtWarningMsg); static inline bool qRectIntersects(const QRect &r1, const QRect &r2) { @@ -1027,23 +1026,6 @@ void QWidgetPrivate::createRecursively() } } -/*! - \internal - Returns the closest parent widget that has a QWindow window handle - - \note This behavior is different from nativeParentWidget(), which - returns the closest parent that has a QWindow window handle with - a created QPlatformWindow, and hence native window (winId). -*/ -QWidget *QWidgetPrivate::closestParentWidgetWithWindowHandle() const -{ - Q_Q(const QWidget); - QWidget *parent = q->parentWidget(); - while (parent && !parent->windowHandle()) - parent = parent->parentWidget(); - return parent; -} - QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const { if (mode == WindowHandleMode::Direct || mode == WindowHandleMode::Closest) { @@ -1053,7 +1035,6 @@ QWindow *QWidgetPrivate::windowHandle(WindowHandleMode mode) const } } if (mode == WindowHandleMode::Closest) { - // FIXME: Use closestParentWidgetWithWindowHandle instead if (auto nativeParent = q_func()->nativeParentWidget()) { if (auto window = nativeParent->windowHandle()) return window; @@ -10880,61 +10861,57 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) setWinId(0); + if (parent != newparent) { + QObjectPrivate::setParent_helper(newparent); //### why does this have to be done in the _sys function??? + if (q->windowHandle()) { + q->windowHandle()->setFlags(f); + QWidget *parentWithWindow = + newparent ? (newparent->windowHandle() ? newparent : newparent->nativeParentWidget()) : nullptr; + if (parentWithWindow) { + QWidget *topLevel = parentWithWindow->window(); + if ((f & Qt::Window) && topLevel && topLevel->windowHandle()) { + q->windowHandle()->setTransientParent(topLevel->windowHandle()); + q->windowHandle()->setParent(nullptr); + } else { + q->windowHandle()->setTransientParent(nullptr); + q->windowHandle()->setParent(parentWithWindow->windowHandle()); + } + } else { + q->windowHandle()->setTransientParent(nullptr); + q->windowHandle()->setParent(nullptr); + } + } + } + if (!newparent) { f |= Qt::Window; if (parent) targetScreen = q->parentWidget()->window()->screen(); } - const bool destroyWindow = ( - // Reparenting top level to child - (oldFlags & Qt::Window) && !(f & Qt::Window) - // And we can dispose of the window - && wasCreated && !q->testAttribute(Qt::WA_NativeWindow) - ); - - if (parent != newparent) { - // Update object parent now, so we can resolve new parent window below - QObjectPrivate::setParent_helper(newparent); - - if (q->windowHandle()) - q->windowHandle()->setFlags(f); - - // If the widget itself or any of its children have been created, - // we need to reparent their QWindows as well. - QWidget *parentWithWindow = closestParentWidgetWithWindowHandle(); - // But if the widget is about to be destroyed we must skip the - // widget itself, and only reparent children. - if (destroyWindow) - reparentWidgetWindowChildren(parentWithWindow); - else - reparentWidgetWindows(parentWithWindow, f); - } - bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); - if (destroyWindow) { + // Reparenting toplevel to child + if (wasCreated && !(f & Qt::Window) && (oldFlags & Qt::Window) && !q->testAttribute(Qt::WA_NativeWindow)) { if (extra && extra->hasWindowContainer) QWindowContainer::toplevelAboutToBeDestroyed(q); - // There shouldn't be any QWindow children left, but if there - // are, re-parent them now, before we destroy. - if (!q->windowHandle()->children().isEmpty()) { - QWidget *parentWithWindow = closestParentWidgetWithWindowHandle(); - QWindow *newParentWindow = parentWithWindow ? parentWithWindow->windowHandle() : nullptr; - for (QObject *child : q->windowHandle()->children()) { - if (QWindow *childWindow = qobject_cast(child)) { - qCWarning(lcWidgetWindow) << "Reparenting" << childWindow - << "before destroying" << this; - childWindow->setParent(newParentWindow); - } - } - } + QWindow *newParentWindow = newparent->windowHandle(); + if (!newParentWindow) + if (QWidget *npw = newparent->nativeParentWidget()) + newParentWindow = npw->windowHandle(); - // We have reparented any child windows of the widget we are - // about to destroy to the new parent window handle, so we can - // safely destroy this widget without destroying sub windows. - q->destroy(true, false); + for (QObject *child : q->windowHandle()->children()) { + QWindow *childWindow = qobject_cast(child); + if (!childWindow) + continue; + + QWidgetWindow *childWW = qobject_cast(childWindow); + QWidget *childWidget = childWW ? childWW->widget() : nullptr; + if (!childWW || (childWidget && childWidget->testAttribute(Qt::WA_NativeWindow))) + childWindow->setParent(newParentWindow); + } + q->destroy(); } adjustFlags(f, q); @@ -10960,53 +10937,6 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) } } -void QWidgetPrivate::reparentWidgetWindows(QWidget *parentWithWindow, Qt::WindowFlags windowFlags) -{ - if (QWindow *window = windowHandle()) { - // Reparent this QWindow, and all QWindow children will follow - if (parentWithWindow) { - // The reparented widget has not updated its window flags yet, - // so we can't ask the widget directly. And we can't use the - // QWindow flags, as unlike QWidgets the QWindow flags always - // reflect Qt::Window, even for child windows. And we can't use - // QWindow::isTopLevel() either, as that depends on the parent, - // which we are in the process of updating. So we propagate the - // new flags of the reparented window from setParent_sys(). - if (windowFlags & Qt::Window) { - // Top level windows can only have transient parents, - // and the transient parent must be another top level. - QWidget *topLevel = parentWithWindow->window(); - auto *transientParent = topLevel->windowHandle(); - Q_ASSERT(transientParent); - qCDebug(lcWidgetWindow) << "Setting" << window << "transient parent to" << transientParent; - window->setTransientParent(transientParent); - window->setParent(nullptr); - } else { - auto *parentWindow = parentWithWindow->windowHandle(); - qCDebug(lcWidgetWindow) << "Reparenting" << window << "into" << parentWindow; - window->setTransientParent(nullptr); - window->setParent(parentWindow); - } - } else { - qCDebug(lcWidgetWindow) << "Making" << window << "top level window"; - window->setTransientParent(nullptr); - window->setParent(nullptr); - } - } else { - reparentWidgetWindowChildren(parentWithWindow); - } -} - -void QWidgetPrivate::reparentWidgetWindowChildren(QWidget *parentWithWindow) -{ - for (auto *child : std::as_const(children)) { - if (auto *childWidget = qobject_cast(child)) { - auto *childPrivate = QWidgetPrivate::get(childWidget); - childPrivate->reparentWidgetWindows(parentWithWindow); - } - } -} - /*! Scrolls the widget including its children \a dx pixels to the right and \a dy downward. Both \a dx and \a dy may be negative. diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index f7206ea23eb..cf0618bca61 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -367,8 +367,6 @@ class Q_WIDGETS_EXPORT QWidgetPrivate : public QObjectPrivate void showChildren(bool spontaneous); void hideChildren(bool spontaneous); void setParent_sys(QWidget *parent, Qt::WindowFlags); - void reparentWidgetWindows(QWidget *parentWithWindow, Qt::WindowFlags windowFlags = {}); - void reparentWidgetWindowChildren(QWidget *parentWithWindow); void scroll_sys(int dx, int dy); void scroll_sys(int dx, int dy, const QRect &r); void deactivateWidgetCleanup(); @@ -635,8 +633,6 @@ class Q_WIDGETS_EXPORT QWidgetPrivate : public QObjectPrivate std::string flagsForDumping() const override; - QWidget *closestParentWidgetWithWindowHandle() const; - // Variables. // Regular pointers (keep them together to avoid gaps on 64 bit architectures). std::unique_ptr extra; diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index ae9c3a0683a..45cc5686faf 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -432,11 +432,6 @@ private slots: void showFullscreenAndroid(); #endif - void setVisibleDuringDestruction(); - - void reparentWindowHandles_data(); - void reparentWindowHandles(); - private: const QString m_platform; QSize m_testWidgetSize; @@ -13370,180 +13365,5 @@ void tst_QWidget::showFullscreenAndroid() } #endif // Q_OS_ANDROID -void tst_QWidget::setVisibleDuringDestruction() -{ - CreateDestroyWidget widget; - widget.create(); - QVERIFY(widget.windowHandle()); - - QSignalSpy signalSpy(widget.windowHandle(), &QWindow::visibleChanged); - EventSpy showEventSpy(widget.windowHandle(), QEvent::Show); - widget.show(); - QTRY_COMPARE(showEventSpy.count(), 1); - QTRY_COMPARE(signalSpy.count(), 1); - - EventSpy hideEventSpy(widget.windowHandle(), QEvent::Hide); - widget.hide(); - QTRY_COMPARE(hideEventSpy.count(), 1); - QTRY_COMPARE(signalSpy.count(), 2); - - widget.show(); - QTRY_COMPARE(showEventSpy.count(), 2); - QTRY_COMPARE(signalSpy.count(), 3); - - widget.destroy(); - QTRY_COMPARE(hideEventSpy.count(), 2); - QTRY_COMPARE(signalSpy.count(), 4); -} - -void tst_QWidget::reparentWindowHandles_data() -{ - QTest::addColumn("stage"); - QTest::addRow("reparent child") << 1; - QTest::addRow("top level to child") << 2; - QTest::addRow("transient parent") << 3; - QTest::addRow("window container") << 4; -} - -void tst_QWidget::reparentWindowHandles() -{ - const bool nativeSiblingsOriginal = qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings); - qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); - auto nativeSiblingGuard = qScopeGuard([&]{ - qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, nativeSiblingsOriginal); - }); - - QFETCH(int, stage); - - switch (stage) { - case 1: { - // Reparent child widget - - QWidget topLevel; - topLevel.setAttribute(Qt::WA_NativeWindow); - QVERIFY(topLevel.windowHandle()); - QPointer child = new QWidget(&topLevel); - child->setAttribute(Qt::WA_DontCreateNativeAncestors); - child->setAttribute(Qt::WA_NativeWindow); - QVERIFY(child->windowHandle()); - - QWidget anotherTopLevel; - anotherTopLevel.setAttribute(Qt::WA_NativeWindow); - QVERIFY(anotherTopLevel.windowHandle()); - QPointer intermediate = new QWidget(&anotherTopLevel); - QPointer leaf = new QWidget(intermediate); - leaf->setAttribute(Qt::WA_DontCreateNativeAncestors); - leaf->setAttribute(Qt::WA_NativeWindow); - QVERIFY(leaf->windowHandle()); - QVERIFY(!intermediate->windowHandle()); - - // Reparenting a native widget should reparent the QWindow - child->setParent(leaf); - QCOMPARE(child->windowHandle()->parent(), leaf->windowHandle()); - QCOMPARE(child->windowHandle()->transientParent(), nullptr); - QVERIFY(!intermediate->windowHandle()); - - // So should reparenting a non-native widget with native children - intermediate->setParent(&topLevel); - QVERIFY(!intermediate->windowHandle()); - QCOMPARE(leaf->windowHandle()->parent(), topLevel.windowHandle()); - QCOMPARE(leaf->windowHandle()->transientParent(), nullptr); - QCOMPARE(child->windowHandle()->parent(), leaf->windowHandle()); - QCOMPARE(child->windowHandle()->transientParent(), nullptr); - } - break; - case 2: { - // Top level to child - - QWidget topLevel; - topLevel.setAttribute(Qt::WA_NativeWindow); - - // A regular top level loses its nativeness - QPointer regularToplevel = new QWidget; - regularToplevel->show(); - QVERIFY(QTest::qWaitForWindowExposed(regularToplevel)); - QVERIFY(regularToplevel->windowHandle()); - regularToplevel->setParent(&topLevel); - QVERIFY(!regularToplevel->windowHandle()); - - // A regular top level loses its nativeness - QPointer regularToplevelWithNativeChildren = new QWidget; - QPointer nativeChild = new QWidget(regularToplevelWithNativeChildren); - nativeChild->setAttribute(Qt::WA_DontCreateNativeAncestors); - nativeChild->setAttribute(Qt::WA_NativeWindow); - QVERIFY(nativeChild->windowHandle()); - regularToplevelWithNativeChildren->show(); - QVERIFY(QTest::qWaitForWindowExposed(regularToplevelWithNativeChildren)); - QVERIFY(regularToplevelWithNativeChildren->windowHandle()); - regularToplevelWithNativeChildren->setParent(&topLevel); - QVERIFY(!regularToplevelWithNativeChildren->windowHandle()); - // But the native child does not - QVERIFY(nativeChild->windowHandle()); - QCOMPARE(nativeChild->windowHandle()->parent(), topLevel.windowHandle()); - - // An explicitly native top level keeps its nativeness, and the window handle moves - QPointer nativeTopLevel = new QWidget; - nativeTopLevel->setAttribute(Qt::WA_NativeWindow); - QVERIFY(nativeTopLevel->windowHandle()); - nativeTopLevel->setParent(&topLevel); - QVERIFY(nativeTopLevel->windowHandle()); - QCOMPARE(nativeTopLevel->windowHandle()->parent(), topLevel.windowHandle()); - } - break; - case 3: { - // Transient parent - - QWidget topLevel; - topLevel.setAttribute(Qt::WA_NativeWindow); - QVERIFY(topLevel.windowHandle()); - QPointer child = new QWidget(&topLevel); - child->setAttribute(Qt::WA_NativeWindow); - QVERIFY(child->windowHandle()); - - QWidget anotherTopLevel; - anotherTopLevel.setAttribute(Qt::WA_NativeWindow); - QVERIFY(anotherTopLevel.windowHandle()); - - // Make transient child of top level - anotherTopLevel.setParent(&topLevel, Qt::Window); - QCOMPARE(anotherTopLevel.windowHandle()->parent(), nullptr); - QCOMPARE(anotherTopLevel.windowHandle()->transientParent(), topLevel.windowHandle()); - - // Make transient child of child - anotherTopLevel.setParent(child, Qt::Window); - QCOMPARE(anotherTopLevel.windowHandle()->parent(), nullptr); - QCOMPARE(anotherTopLevel.windowHandle()->transientParent(), topLevel.windowHandle()); - } - break; - case 4: { - // Window container - - QWidget topLevel; - topLevel.setAttribute(Qt::WA_NativeWindow); - QVERIFY(topLevel.windowHandle()); - - QPointer child = new QWidget(&topLevel); - QVERIFY(!child->windowHandle()); - - QWindow *window = new QWindow; - QWidget *container = QWidget::createWindowContainer(window); - container->setParent(child); - topLevel.show(); - QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); - QCOMPARE(window->parent(), topLevel.windowHandle()); - - QWidget anotherTopLevel; - anotherTopLevel.setAttribute(Qt::WA_NativeWindow); - QVERIFY(anotherTopLevel.windowHandle()); - - child->setParent(&anotherTopLevel); - QCOMPARE(window->parent(), anotherTopLevel.windowHandle()); - } - break; - default: - Q_UNREACHABLE(); - } -} - QTEST_MAIN(tst_QWidget) #include "tst_qwidget.moc"