QFocusFrame: protect it from being moved around by accident

If a focus frame is set around a widget that exist inside a
QAbstractItemView, both the focus frame and the widget will
be scrolled when the table is scrolled (since the focus frame
is a child of the view). The result is that after the widget
has been scrolled (which will move the focus frame to the
correct position as well), the focus frame will be scrolled
next, and therefore away from the widget.

This patch will catch this case by always adjusting the
focus frame position when someone tries to move it. Trying
to move the focus frame away from the widget it tracks
will anyway be flaky.

Fixes: QTBUG-63877
Change-Id: Ic2aacc4fafc219280e32092c258a7539d0db9cd0
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
bb10
Richard Moe Gustavsen 2018-11-08 14:11:11 +01:00
parent a5e32f9375
commit f1812aad89
2 changed files with 58 additions and 1 deletions

View File

@ -335,7 +335,25 @@ QFocusFrame::eventFilter(QObject *o, QEvent *e)
/*! \reimp */
bool QFocusFrame::event(QEvent *e)
{
return QWidget::event(e);
Q_D(QFocusFrame);
switch (e->type()) {
case QEvent::Move:
case QEvent::Resize:
if (d->widget) {
// When we're tracking a widget, we don't allow anyone to move the focus frame around.
// We do our best with event filters to make it stay on top of the widget, so trying to
// move the frame somewhere else will be flaky at best. This can e.g happen for general
// purpose code, like QAbstractScrollView, that bulk-moves all children a certain distance.
// So we need to call updateSize() when that happens to ensure that the focus frame stays
// on top of the widget.
d->updateSize();
}
break;
default:
return QWidget::event(e);
}
return true;
}
QT_END_NAMESPACE

View File

@ -32,6 +32,8 @@
#include <qcoreapplication.h>
#include <qdebug.h>
#include <qfocusframe.h>
#include <qtableview.h>
#include <qstandarditemmodel.h>
class tst_QFocusFrame : public QObject
{
@ -43,6 +45,7 @@ public:
private slots:
void getSetCheck();
void focusFrameInsideScrollview();
};
tst_QFocusFrame::tst_QFocusFrame()
@ -68,5 +71,41 @@ void tst_QFocusFrame::getSetCheck()
delete obj1;
}
void tst_QFocusFrame::focusFrameInsideScrollview()
{
// Make sure that the focus frame follows the widget, even
// if the widget is inside a QAbstractItemView. A QAbstractItemView will scroll
// all the children, including the focus frame, when it scrolls, which
// is why special considerations are taken inside the focus frame to
// prevent the frame to scroll away from the widget it tracks.
if (qApp->style()->objectName() != QLatin1String("macintosh"))
QSKIP("This test is only valid when using a style that has a focus frame");
QWidget window;
window.setGeometry(100, 100, 500, 500);
QTableView tableView(&window);
tableView.resize(window.size());
QStandardItemModel *itemModel = new QStandardItemModel();
for (int i = 0; i < 50; ++i)
itemModel->appendRow(new QStandardItem("Value"));
tableView.setModel(itemModel);
tableView.edit(itemModel->index(8, 0));
window.show();
QFocusFrame *focusFrame = nullptr;
QTRY_VERIFY(focusFrame = window.findChild<QFocusFrame *>());
const QPoint initialOffset = focusFrame->widget()->mapToGlobal(QPoint()) - focusFrame->mapToGlobal(QPoint());
tableView.scrollTo(itemModel->index(40, 0));
QPoint offsetAfterScroll = focusFrame->widget()->mapToGlobal(QPoint()) - focusFrame->mapToGlobal(QPoint());
QCOMPARE(offsetAfterScroll, initialOffset);
tableView.scrollTo(itemModel->index(0, 0));
offsetAfterScroll = focusFrame->widget()->mapToGlobal(QPoint()) - focusFrame->mapToGlobal(QPoint());
QCOMPARE(offsetAfterScroll, initialOffset);
}
QTEST_MAIN(tst_QFocusFrame)
#include "tst_qfocusframe.moc"