Fix crash when the focus widget gets a focus proxy after the fact
QApplicationPrivate::focus_widget became a dangling pointer in the following scenario: A widget first gets focus and later on gets a focus proxy. QApplicationPrivate::focus_widget was still pointing to the initial widget. Upon destruction, QWidget::hasFocus() [which follows to the focus proxy and then compares with focus_widget] was therefore false for both widgets. So QWidget::clearFocus() didn't call QApplicationPrivate::setFocusWidget(0) for either of them. As a result, focus_widget remained set, and became dangling. In real life, this happened with a QWebEngineView, which the application gave focus to upon creation. At that time it doesn't have a focus proxy yet. That happens later, in QWebEngineViewPrivate::widgetChanged. https://bugs.kde.org/show_bug.cgi?id=381793 Change-Id: Ifee610bb76a2d4d2797b98ece9bffe5fffe3c6a6 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
f53fc76060
commit
3e7463411e
@ -6436,8 +6436,18 @@ void QWidget::setFocusProxy(QWidget * w)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget *oldDeepestFocusProxy = d_func()->deepestFocusProxy();
|
||||||
|
if (!oldDeepestFocusProxy)
|
||||||
|
oldDeepestFocusProxy = this;
|
||||||
|
const bool changingAppFocusWidget = (QApplicationPrivate::focus_widget == oldDeepestFocusProxy);
|
||||||
|
|
||||||
d->createExtra();
|
d->createExtra();
|
||||||
d->extra->focus_proxy = w;
|
d->extra->focus_proxy = w;
|
||||||
|
|
||||||
|
if (changingAppFocusWidget) {
|
||||||
|
QWidget *newDeepestFocusProxy = d_func()->deepestFocusProxy();
|
||||||
|
QApplicationPrivate::focus_widget = newDeepestFocusProxy ? newDeepestFocusProxy : this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -181,6 +181,8 @@ private slots:
|
|||||||
void tabOrderWithCompoundWidgets();
|
void tabOrderWithCompoundWidgets();
|
||||||
void tabOrderNoChange();
|
void tabOrderNoChange();
|
||||||
void tabOrderNoChange2();
|
void tabOrderNoChange2();
|
||||||
|
void appFocusWidgetWithFocusProxyLater();
|
||||||
|
void appFocusWidgetWhenLosingFocusProxy();
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||||
void activation();
|
void activation();
|
||||||
#endif
|
#endif
|
||||||
@ -2031,6 +2033,51 @@ void tst_QWidget::tabOrderNoChange2()
|
|||||||
QCOMPARE(focusChainBackward, getFocusChain(&w, false));
|
QCOMPARE(focusChainBackward, getFocusChain(&w, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QWidget::appFocusWidgetWithFocusProxyLater()
|
||||||
|
{
|
||||||
|
// Given a lineedit without a focus proxy
|
||||||
|
QWidget window;
|
||||||
|
window.setWindowTitle(QTest::currentTestFunction());
|
||||||
|
QLineEdit *lineEditFocusProxy = new QLineEdit(&window);
|
||||||
|
QLineEdit *lineEdit = new QLineEdit(&window);
|
||||||
|
lineEdit->setFocus();
|
||||||
|
window.show();
|
||||||
|
QApplication::setActiveWindow(&window);
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
||||||
|
QCOMPARE(QApplication::focusWidget(), lineEdit);
|
||||||
|
|
||||||
|
// When setting a focus proxy for the focus widget (like QWebEngineView does)
|
||||||
|
lineEdit->setFocusProxy(lineEditFocusProxy);
|
||||||
|
|
||||||
|
// Then the focus widget should be updated
|
||||||
|
QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy);
|
||||||
|
|
||||||
|
// So that deleting the lineEdit and later the window, doesn't crash
|
||||||
|
delete lineEdit;
|
||||||
|
QCOMPARE(QApplication::focusWidget(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QWidget::appFocusWidgetWhenLosingFocusProxy()
|
||||||
|
{
|
||||||
|
// Given a lineedit with a focus proxy
|
||||||
|
QWidget window;
|
||||||
|
window.setWindowTitle(QTest::currentTestFunction());
|
||||||
|
QLineEdit *lineEditFocusProxy = new QLineEdit(&window);
|
||||||
|
QLineEdit *lineEdit = new QLineEdit(&window);
|
||||||
|
lineEdit->setFocusProxy(lineEditFocusProxy);
|
||||||
|
lineEdit->setFocus();
|
||||||
|
window.show();
|
||||||
|
QApplication::setActiveWindow(&window);
|
||||||
|
QVERIFY(QTest::qWaitForWindowActive(&window));
|
||||||
|
QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy);
|
||||||
|
|
||||||
|
// When unsetting the focus proxy
|
||||||
|
lineEdit->setFocusProxy(nullptr);
|
||||||
|
|
||||||
|
// Then the application focus widget should be back to the lineedit
|
||||||
|
QCOMPARE(QApplication::focusWidget(), lineEdit);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||||
void tst_QWidget::activation()
|
void tst_QWidget::activation()
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user