QDialog: respect WA_ShowWithoutActivating

QDialog overrides setVisible to set focus on the default push button, or
(if there is no such button), make the first autoDefault push button the
default button. QDialog also explicitly sends a FocusIn event to the
focus widget in the dialog.

All this should not be done if the dialog does not become active after
getting shown, which will be prevented if the WA_ShowWithoutActivating
attribute is set.

Add a test case.

Fixes: QTBUG-8857
Pick-to: 6.2
Change-Id: If47021a4721a280ba45e95b43d0424cdacd9b4ea
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
bb10
Volker Hilsheimer 2021-10-10 09:32:32 +02:00
parent 3fc58a5ed8
commit 44b9aec8d9
2 changed files with 65 additions and 31 deletions

View File

@ -800,42 +800,47 @@ void QDialog::setVisible(bool visible)
return;
QWidget::setVisible(visible);
QWidget *fw = window()->focusWidget();
if (!fw)
fw = this;
/*
The following block is to handle a special case, and does not
really follow propper logic in concern of autoDefault and TAB
order. However, it's here to ease usage for the users. If a
dialog has a default QPushButton, and first widget in the TAB
order also is a QPushButton, then we give focus to the main
default QPushButton. This simplifies code for the developers,
and actually catches most cases... If not, then they simply
have to use [widget*]->setFocus() themselves...
*/
// Window activation might be prevented. We can't test isActiveWindow here,
// as the window will be activated asynchronously by the window manager.
if (!testAttribute(Qt::WA_ShowWithoutActivating)) {
QWidget *fw = window()->focusWidget();
if (!fw)
fw = this;
/*
The following block is to handle a special case, and does not
really follow propper logic in concern of autoDefault and TAB
order. However, it's here to ease usage for the users. If a
dialog has a default QPushButton, and first widget in the TAB
order also is a QPushButton, then we give focus to the main
default QPushButton. This simplifies code for the developers,
and actually catches most cases... If not, then they simply
have to use [widget*]->setFocus() themselves...
*/
#if QT_CONFIG(pushbutton)
if (d->mainDef && fw->focusPolicy() == Qt::NoFocus) {
QWidget *first = fw;
while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus)
;
if (first != d->mainDef && qobject_cast<QPushButton*>(first))
d->mainDef->setFocus();
}
if (!d->mainDef && isWindow()) {
QWidget *w = fw;
while ((w = w->nextInFocusChain()) != fw) {
QPushButton *pb = qobject_cast<QPushButton *>(w);
if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) {
pb->setDefault(true);
break;
if (d->mainDef && fw->focusPolicy() == Qt::NoFocus) {
QWidget *first = fw;
while ((first = first->nextInFocusChain()) != fw && first->focusPolicy() == Qt::NoFocus)
;
if (first != d->mainDef && qobject_cast<QPushButton*>(first))
d->mainDef->setFocus();
}
if (!d->mainDef && isWindow()) {
QWidget *w = fw;
while ((w = w->nextInFocusChain()) != fw) {
QPushButton *pb = qobject_cast<QPushButton *>(w);
if (pb && pb->autoDefault() && pb->focusPolicy() != Qt::NoFocus) {
pb->setDefault(true);
break;
}
}
}
}
#endif
if (fw && !fw->hasFocus()) {
QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason);
QCoreApplication::sendEvent(fw, &e);
if (fw && !fw->hasFocus()) {
QFocusEvent e(QEvent::FocusIn, Qt::TabFocusReason);
QCoreApplication::sendEvent(fw, &e);
}
}
#ifndef QT_NO_ACCESSIBILITY

View File

@ -70,6 +70,8 @@ private slots:
void showMinimized();
void showFullScreen();
void showAsTool();
void showWithoutActivating_data();
void showWithoutActivating();
void toolDialogPosition();
void deleteMainDefault();
void deleteInExec();
@ -323,6 +325,33 @@ void tst_QDialog::showAsTool()
}
}
void tst_QDialog::showWithoutActivating_data()
{
QTest::addColumn<bool>("showWithoutActivating");
QTest::addColumn<int>("focusInCount");
QTest::addRow("showWithoutActivating") << true << 0;
QTest::addRow("showWithActivating") << false << 1;
}
void tst_QDialog::showWithoutActivating()
{
QFETCH(bool, showWithoutActivating);
QFETCH(int, focusInCount);
struct Dialog : public QDialog
{
int focusInCount = 0;
protected:
void focusInEvent(QFocusEvent *) override { ++focusInCount; }
} dialog;
dialog.setAttribute(Qt::WA_ShowWithoutActivating, showWithoutActivating);
dialog.show();
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
QCOMPARE(dialog.focusInCount, focusInCount);
}
// Verify that pos() returns the same before and after show()
// for a dialog with the Tool window type.
void tst_QDialog::toolDialogPosition()