Widgets: report focus object change to QtGui before sending widget events

Updating the focus child means the focus object of the window has changed.
We need to report this to QtGui immediately so that it can e.g. inform the
input context of the new focus object, before widgets reacting to the focus
events start calling update() on the input method.

Change-Id: Ie3f7b835591e71519e3f384c2abdad53242c9736
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Tor Arne Vestbø 2017-01-03 17:38:55 +01:00
parent 166f23eb92
commit 6f504a1cdd
2 changed files with 73 additions and 18 deletions

View File

@ -6595,11 +6595,6 @@ void QWidget::setFocus(Qt::FocusReason reason)
} else {
f->d_func()->updateFocusChild();
}
if (QTLWExtra *extra = f->window()->d_func()->maybeTopData()) {
if (extra->window)
emit extra->window->focusObjectChanged(f);
}
}
void QWidgetPrivate::setFocus_sys()
@ -6634,6 +6629,11 @@ void QWidgetPrivate::updateFocusChild()
w = w->isWindow() ? 0 : w->parentWidget();
}
}
if (QTLWExtra *extra = q->window()->d_func()->maybeTopData()) {
if (extra->window)
emit extra->window->focusObjectChanged(q);
}
}
/*!
@ -6675,9 +6675,15 @@ void QWidget::clearFocus()
w->d_func()->focus_child = 0;
w = w->parentWidget();
}
// Since focus_child is the basis for the top level QWidgetWindow's focusObject()
// we need to report this change to the rest of Qt, but we match setFocus() and
// do it at the end of the function.
// Since we've unconditionally cleared the focus_child of our parents, we need
// to report this to the rest of Qt. Note that the focus_child is not the same
// thing as the application's focusWidget, which is why this piece of code is
// not inside the hasFocus() block below.
if (QTLWExtra *extra = window()->d_func()->maybeTopData()) {
if (extra->window)
emit extra->window->focusObjectChanged(extra->window->focusObject());
}
#ifndef QT_NO_GRAPHICSVIEW
QWExtra *topData = d_func()->extra;
@ -6700,15 +6706,6 @@ void QWidget::clearFocus()
#endif
}
}
// Since we've unconditionally cleared the focus_child of our parents, we need
// to report this to the rest of Qt. Note that the focus_child is not the same
// thing as the application's focusWidget, which is why this piece of code is
// not inside the hasFocus() block above.
if (QTLWExtra *extra = window()->d_func()->maybeTopData()) {
if (extra->window)
emit extra->window->focusObjectChanged(extra->window->focusObject());
}
}

View File

@ -5216,16 +5216,40 @@ protected:
widgetDuringFocusAboutToChange = qApp->focusWidget();
return QWidget::event(ev);
}
virtual void focusInEvent(QFocusEvent *)
{
foucsObjectDuringFocusIn = qApp->focusObject();
detectedBadEventOrdering = foucsObjectDuringFocusIn != mostRecentFocusObjectChange;
}
virtual void focusOutEvent(QFocusEvent *)
{
foucsObjectDuringFocusOut = qApp->focusObject();
widgetDuringFocusOut = qApp->focusWidget();
detectedBadEventOrdering = foucsObjectDuringFocusOut != mostRecentFocusObjectChange;
}
void focusObjectChanged(QObject *focusObject)
{
mostRecentFocusObjectChange = focusObject;
}
public:
FocusWidget(QWidget *parent) : QWidget(parent), widgetDuringFocusAboutToChange(0), widgetDuringFocusOut(0) {}
FocusWidget(QWidget *parent) : QWidget(parent),
widgetDuringFocusAboutToChange(0), widgetDuringFocusOut(0),
foucsObjectDuringFocusIn(0), foucsObjectDuringFocusOut(0),
mostRecentFocusObjectChange(0), detectedBadEventOrdering(false)
{
connect(qGuiApp, &QGuiApplication::focusObjectChanged, this, &FocusWidget::focusObjectChanged);
}
QWidget *widgetDuringFocusAboutToChange;
QWidget *widgetDuringFocusOut;
QObject *foucsObjectDuringFocusIn;
QObject *foucsObjectDuringFocusOut;
QObject *mostRecentFocusObjectChange;
bool detectedBadEventOrdering;
};
void tst_QWidget::setFocus()
@ -5426,6 +5450,40 @@ void tst_QWidget::setFocus()
QCOMPARE(window.focusWidget(), nullptr);
QCOMPARE(QApplication::focusWidget(), nullptr);
}
{
QWidget window;
window.resize(m_testWidgetSize);
window.move(windowPos);
FocusWidget child1(&window);
QWidget child2(&window);
window.show();
window.activateWindow();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QTRY_VERIFY(QApplication::focusWindow());
QCOMPARE(QApplication::focusObject(), &window);
child1.setFocus();
QTRY_VERIFY(child1.hasFocus());
QCOMPARE(window.focusWidget(), &child1);
QCOMPARE(QApplication::focusWidget(), &child1);
QCOMPARE(QApplication::focusObject(), &child1);
QCOMPARE(child1.foucsObjectDuringFocusIn, &child1);
QVERIFY2(!child1.detectedBadEventOrdering,
"focusObjectChanged should be delivered before widget focus events on setFocus");
child1.clearFocus();
QTRY_VERIFY(!child1.hasFocus());
QCOMPARE(window.focusWidget(), nullptr);
QCOMPARE(QApplication::focusWidget(), nullptr);
QCOMPARE(QApplication::focusObject(), &window);
QVERIFY(child1.foucsObjectDuringFocusOut != &child1);
QVERIFY2(!child1.detectedBadEventOrdering,
"focusObjectChanged should be delivered before widget focus events on clearFocus");
}
}
template<class T> class EventSpy : public QObject