From 886d1f5b69b7575c1ffb89029bab63146d4d176e Mon Sep 17 00:00:00 2001 From: Senthil Kannan Kandaswamy Date: Wed, 24 Jul 2024 18:15:54 -0400 Subject: [PATCH] Revert "MAYA-132611: Crash when docking the Select a light source (#102)" (#105) #28746 was made to use an older artifact for Maya 2025.2 to save precious hours that would have been otherwise spent on waiting for the Qt build to complete. This PR aims to solve it properly by reverting the offending change specifically. This also allows us to resolve MAYA-132068 This reverts commit aaca5d7. --- src/widgets/kernel/qwidget.cpp | 148 ++++---------- src/widgets/kernel/qwidget_p.h | 4 - .../widgets/kernel/qwidget/tst_qwidget.cpp | 180 ------------------ 3 files changed, 39 insertions(+), 293 deletions(-) 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"