diff --git a/src/widgets/widgets/qdialogbuttonbox.cpp b/src/widgets/widgets/qdialogbuttonbox.cpp index 69fea049145..c35f8c8edcf 100644 --- a/src/widgets/widgets/qdialogbuttonbox.cpp +++ b/src/widgets/widgets/qdialogbuttonbox.cpp @@ -704,32 +704,43 @@ QDialogButtonBox::ButtonRole QDialogButtonBox::buttonRole(QAbstractButton *butto void QDialogButtonBox::removeButton(QAbstractButton *button) { Q_D(QDialogButtonBox); - d->removeButton(button, QDialogButtonBoxPrivate::RemoveRule::Disconnect); + d->removeButton(button, QDialogButtonBoxPrivate::RemoveReason::ManualRemove); } -void QDialogButtonBoxPrivate::removeButton(QAbstractButton *button, RemoveRule rule) +/*! + \internal + Removes \param button. + \param reason determines the behavior following the removal: + \list + \li \c ManualRemove disconnects all signals and removes the button from standardButtonHash. + \li \c HideEvent keeps connections alive, standard buttons remain in standardButtonHash. + \li \c Destroyed removes the button from standardButtonHash. Signals remain untouched, because + the button might already be only a QObject, the destructor of which handles disconnecting. + \endlist + */ +void QDialogButtonBoxPrivate::removeButton(QAbstractButton *button, RemoveReason reason) { if (!button) return; - // Remove it from hidden buttons + // Remove button from hidden buttons and roles hiddenButtons.remove(button); - - // Remove it from the standard button hash first and then from the roles - standardButtonHash.remove(reinterpret_cast(button)); for (int i = 0; i < QDialogButtonBox::NRoles; ++i) buttonLists[i].removeOne(button); - switch (rule) { - case RemoveRule::Disconnect: + switch (reason) { + case RemoveReason::ManualRemove: button->setParent(nullptr); QObjectPrivate::disconnect(button, &QAbstractButton::clicked, this, &QDialogButtonBoxPrivate::handleButtonClicked); QObjectPrivate::disconnect(button, &QAbstractButton::destroyed, this, &QDialogButtonBoxPrivate::handleButtonDestroyed); button->removeEventFilter(filter.get()); + Q_FALLTHROUGH(); + case RemoveReason::Destroyed: + standardButtonHash.remove(reinterpret_cast(button)); break; - case RemoveRule::KeepConnections: + case RemoveReason::HideEvent: break; } } @@ -881,7 +892,7 @@ void QDialogButtonBoxPrivate::handleButtonDestroyed() { Q_Q(QDialogButtonBox); if (QObject *object = q->sender()) - removeButton(reinterpret_cast(object), RemoveRule::KeepConnections); + removeButton(reinterpret_cast(object), RemoveReason::Destroyed); } bool QDialogButtonBoxPrivate::handleButtonShowAndHide(QAbstractButton *button, QEvent *event) @@ -897,7 +908,7 @@ bool QDialogButtonBoxPrivate::handleButtonShowAndHide(QAbstractButton *button, Q case QEvent::HideToParent: { const QDialogButtonBox::ButtonRole role = q->buttonRole(button); if (role != QDialogButtonBox::ButtonRole::InvalidRole) { - removeButton(button, RemoveRule::KeepConnections); + removeButton(button, RemoveReason::HideEvent); hiddenButtons.insert(button, role); layoutButtons(); } diff --git a/src/widgets/widgets/qdialogbuttonbox_p.h b/src/widgets/widgets/qdialogbuttonbox_p.h index 1aa60712412..c3d7e034894 100644 --- a/src/widgets/widgets/qdialogbuttonbox_p.h +++ b/src/widgets/widgets/qdialogbuttonbox_p.h @@ -25,9 +25,10 @@ class Q_AUTOTEST_EXPORT QDialogButtonBoxPrivate : public QWidgetPrivate Q_DECLARE_PUBLIC(QDialogButtonBox) public: - enum class RemoveRule { - KeepConnections, - Disconnect, + enum class RemoveReason { + HideEvent, + ManualRemove, + Destroyed, }; enum class LayoutRule { DoLayout, @@ -53,7 +54,7 @@ public: void createStandardButtons(QDialogButtonBox::StandardButtons buttons); - void removeButton(QAbstractButton *button, RemoveRule rule); + void removeButton(QAbstractButton *button, RemoveReason reason); void layoutButtons(); void initLayout(); void resetLayout(); diff --git a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp index b3f26e5dd81..3f5ffa687ba 100644 --- a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp +++ b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp @@ -55,6 +55,7 @@ private slots: #ifdef QT_BUILD_INTERNAL void hideAndShowButton(); #endif + void hideAndShowStandardButton(); void buttonRole_data(); void buttonRole(); void setStandardButtons_data(); @@ -426,6 +427,22 @@ void tst_QDialogButtonBox::hideAndShowButton() } #endif +void tst_QDialogButtonBox::hideAndShowStandardButton() +{ + QDialogButtonBox buttonBox; + buttonBox.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttonBox.show(); + QVERIFY(QTest::qWaitForWindowExposed(&buttonBox)); + auto *button = buttonBox.button(QDialogButtonBox::Cancel); + QVERIFY(button); + button->hide(); + QVERIFY(QTest::qWaitFor([button](){ return !button->isVisible(); })); + QCOMPARE(button, buttonBox.button(QDialogButtonBox::Cancel)); + button->show(); + QVERIFY(QTest::qWaitForWindowExposed(button)); + QCOMPARE(button, buttonBox.button(QDialogButtonBox::Cancel)); +} + void tst_QDialogButtonBox::testDelete() { QDialogButtonBox buttonBox;