summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAxel Spoerl <[email protected]>2024-01-25 17:07:34 +0100
committerQt Cherry-pick Bot <[email protected]>2024-03-16 18:16:19 +0000
commit42185e58f16960d96aa72054d866cbf2c6f3305e (patch)
tree9f714adaf9bbf086838dd554e45dc2b64cd50e25
parentd40c5d02e7135c6910732280d344f0c493a7539a (diff)
QDialogButtonBox: Don't set focus in a dialog with StrongFocus children
A QDialogButtonBox with the first accept button becoming default, didn't explicitly set focus on such a button in a QDialog. d44413d526ec12ed83acd7343c2005782178c7ad implemented this missing functionality. It set focus to the automatic default button, unless the QDialog had a focusWidget() set. That has caused a regression, in cases where - the QDialog has a QWidget child with a Qt::StrongFocus policy, and - the QDialog is not yet visible, so focusWidget() returns nullptr. Amend d44413d526ec12ed83acd7343c2005782178c7ad: Implement a helper in QWidgetPrivate, that returns true, if a child with a given focus policy is found. Do not set focus to a QDialogButtonBox's automatic default button, when - not located inside a QDialog, or - a focusWidget() exists, or - the dialog has QWidget child with Qt::StrongFocus, that is not a child of the QDialogButtonBox. Add an autotest function. Fixes: QTBUG-121514 Fixes: QTBUG-120049 Change-Id: I3c65ae36b56657f9af4a3a4b42f9b66e8bc5c534 Reviewed-by: Tor Arne Vestbø <[email protected]> (cherry picked from commit 78a3301372fb9b48dc65b18a19731db37abab75c) Reviewed-by: Qt Cherry-pick Bot <[email protected]> (cherry picked from commit 1e89a6e587637b9ede469f5d49fda62e2f1aa54e) (cherry picked from commit 281a41b81ea52d4744e1344c77534dff3eae1f63)
-rw-r--r--src/widgets/kernel/qwidget.cpp18
-rw-r--r--src/widgets/kernel/qwidget_p.h1
-rw-r--r--src/widgets/widgets/qdialogbuttonbox.cpp6
-rw-r--r--tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp37
4 files changed, 60 insertions, 2 deletions
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 75fb63381e5..c1abd8c5dcc 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -13142,6 +13142,24 @@ void QWidgetPrivate::setNetWmWindowTypes(bool skipIfMissing)
#endif
}
+/*!
+ \internal
+ \return \c true, if a child with \param policy exists and isn't a child of \param excludeChildrenOf.
+ Return false otherwise.
+ */
+bool QWidgetPrivate::hasChildWithFocusPolicy(Qt::FocusPolicy policy, const QWidget *excludeChildrenOf) const
+{
+ Q_Q(const QWidget);
+ const QWidgetList &children = q->findChildren<QWidget *>(Qt::FindChildrenRecursively);
+ for (const auto *child : children) {
+ if (child->focusPolicy() == policy && child->isEnabled()
+ && (!excludeChildrenOf || !excludeChildrenOf->isAncestorOf(child))) {
+ return true;
+ }
+ }
+ return false;
+}
+
#ifndef QT_NO_DEBUG_STREAM
static inline void formatWidgetAttributes(QDebug debug, const QWidget *widget)
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index fba2053349f..53e1b9c5e11 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -740,6 +740,7 @@ public:
bool stealKeyboardGrab(bool grab);
bool stealMouseGrab(bool grab);
+ bool hasChildWithFocusPolicy(Qt::FocusPolicy policy, const QWidget *excludeChildrenOf = nullptr) const;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QWidgetPrivate::DrawWidgetFlags)
diff --git a/src/widgets/widgets/qdialogbuttonbox.cpp b/src/widgets/widgets/qdialogbuttonbox.cpp
index 7f845f12504..b2ce4f66732 100644
--- a/src/widgets/widgets/qdialogbuttonbox.cpp
+++ b/src/widgets/widgets/qdialogbuttonbox.cpp
@@ -1023,8 +1023,10 @@ void QDialogButtonBoxPrivate::ensureFirstAcceptIsDefault()
// focus proxy/first button stealing the default button status
// immediately when the button box is focused, which is not what
// we want. Account for this by explicitly making the firstAcceptButton
- // focused as well, unless an explicit focus widget has been set.
- if (dialog && !dialog->focusWidget())
+ // focused as well, unless an explicit focus widget has been set, or
+ // a dialog child has Qt::StrongFocus.
+ if (dialog && !(QWidgetPrivate::get(dialog)->hasChildWithFocusPolicy(Qt::StrongFocus, q)
+ || dialog->focusWidget()))
firstAcceptButton->setFocus();
}
}
diff --git a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp
index 9f405a8bebe..83abf0e8e36 100644
--- a/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp
+++ b/tests/auto/widgets/widgets/qdialogbuttonbox/tst_qdialogbuttonbox.cpp
@@ -6,6 +6,7 @@
#include <QtWidgets/QStyle>
#include <QtWidgets/QLayout>
#include <QtWidgets/QDialog>
+#include <QtWidgets/QLineEdit>
#include <QtGui/QAction>
#include <QtGui/QStyleHints>
#include <qdialogbuttonbox.h>
@@ -82,6 +83,8 @@ private slots:
void task191642_default();
void testDeletedStandardButton();
void automaticDefaultButton();
+ void initialFocus_data();
+ void initialFocus();
private:
qint64 timeStamp;
@@ -958,5 +961,39 @@ void tst_QDialogButtonBox::automaticDefaultButton()
}
}
+void tst_QDialogButtonBox::initialFocus_data()
+{
+ QTest::addColumn<Qt::FocusPolicy>("focusPolicy");
+ QTest::addColumn<bool>("lineEditHasFocus");
+
+ QTest::addRow("TabFocus") << Qt::FocusPolicy::TabFocus << false;
+ QTest::addRow("StrongFocus") << Qt::FocusPolicy::StrongFocus << true;
+ QTest::addRow("NoFocus") << Qt::FocusPolicy::NoFocus << false;
+ QTest::addRow("ClickFocus") << Qt::FocusPolicy::ClickFocus << false;
+ QTest::addRow("WheelFocus") << Qt::FocusPolicy::WheelFocus << false;
+}
+
+void tst_QDialogButtonBox::initialFocus()
+{
+ QFETCH(const Qt::FocusPolicy, focusPolicy);
+ QFETCH(const bool, lineEditHasFocus);
+ QDialog dialog;
+ QVBoxLayout *layout = new QVBoxLayout(&dialog);
+ QLineEdit *lineEdit = new QLineEdit(&dialog);
+ lineEdit->setFocusPolicy(focusPolicy);
+ layout->addWidget(lineEdit);
+ QDialogButtonBox *dialogButtonBox = new QDialogButtonBox(&dialog);
+ layout->addWidget(dialogButtonBox);
+ dialogButtonBox->addButton(QDialogButtonBox::Reset);
+ const auto *firstAcceptButton = dialogButtonBox->addButton(QDialogButtonBox::Ok);
+ dialogButtonBox->addButton(QDialogButtonBox::Cancel);
+ dialog.show();
+ dialog.activateWindow();
+ if (lineEditHasFocus)
+ QTRY_VERIFY(lineEdit->hasFocus());
+ else
+ QTRY_VERIFY(firstAcceptButton->hasFocus());
+}
+
QTEST_MAIN(tst_QDialogButtonBox)
#include "tst_qdialogbuttonbox.moc"