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
Change-Id: I3c65ae36b56657f9af4a3a4b42f9b66e8bc5c534
Reviewed-by: Jani Heikkinen <jani.heikkinen@qt.io>
This commit is contained in:
Axel Spoerl 2024-01-25 17:07:34 +01:00
parent 0710865eed
commit 1b23260624
4 changed files with 60 additions and 2 deletions

View File

@ -13156,6 +13156,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)

View File

@ -737,6 +737,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)

View File

@ -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();
}
}

View File

@ -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"