QWidget::mapTo/FromGlobal(): Rewrite using a QTransform.

Introduce a function going up the widget hierarchy determining a QTransform
for mapping the coordinates applying the transformations of any QGraphicsView
instances found. In mapFromGlobal(), use the inverse of it. This fixes the
case of widget hierarchies embedded into QGraphicsView with transformations.

Increase fuzz in the tests due to float rounding errors.

Task-number: QTBUG-41135
Task-number: QTBUG-50030
Task-number: QTBUG-50136
Task-number: QTBUG-52507
Change-Id: I507e0bccd546250fe9c2d1b74ef38657d61490b4
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
This commit is contained in:
Friedemann Kleint 2016-01-21 08:15:18 +01:00 committed by Friedemann Kleint
parent a26435d65c
commit 474af0a61d
2 changed files with 62 additions and 54 deletions

View File

@ -12346,6 +12346,53 @@ static inline bool canMapPosition(QWindow *window)
return window->handle() && !qt_window_private(window)->resizeEventPending;
}
#ifndef QT_NO_GRAPHICSVIEW
static inline QGraphicsProxyWidget *graphicsProxyWidget(const QWidget *w)
{
QGraphicsProxyWidget *result = Q_NULLPTR;
const QWidgetPrivate *d = qt_widget_private(const_cast<QWidget *>(w));
if (d->extra)
result = d->extra->proxyWidget;
return result;
}
#endif // !QT_NO_GRAPHICSVIEW
struct MapToGlobalTransformResult {
QTransform transform;
QWindow *window;
};
static MapToGlobalTransformResult mapToGlobalTransform(const QWidget *w)
{
MapToGlobalTransformResult result;
result.window = Q_NULLPTR;
for ( ; w ; w = w->parentWidget()) {
#ifndef QT_NO_GRAPHICSVIEW
if (QGraphicsProxyWidget *qgpw = graphicsProxyWidget(w)) {
if (const QGraphicsScene *scene = qgpw->scene()) {
const QList <QGraphicsView *> views = scene->views();
if (!views.isEmpty()) {
result.transform *= qgpw->sceneTransform();
result.transform *= views.first()->viewportTransform();
w = views.first()->viewport();
}
}
}
#endif // !QT_NO_GRAPHICSVIEW
QWindow *window = w->windowHandle();
if (window && canMapPosition(window)) {
result.window = window;
break;
}
const QPoint topLeft = w->geometry().topLeft();
result.transform.translate(topLeft.x(), topLeft.y());
if (w->isWindow())
break;
}
return result;
}
/*!
\fn QPoint QWidget::mapToGlobal(const QPoint &pos) const
@ -12357,29 +12404,9 @@ static inline bool canMapPosition(QWindow *window)
*/
QPoint QWidget::mapToGlobal(const QPoint &pos) const
{
#ifndef QT_NO_GRAPHICSVIEW
Q_D(const QWidget);
if (d->extra && d->extra->proxyWidget && d->extra->proxyWidget->scene()) {
const QList <QGraphicsView *> views = d->extra->proxyWidget->scene()->views();
if (!views.isEmpty()) {
const QPointF scenePos = d->extra->proxyWidget->mapToScene(pos);
const QPoint viewPortPos = views.first()->mapFromScene(scenePos);
return views.first()->viewport()->mapToGlobal(viewPortPos);
}
}
#endif // !QT_NO_GRAPHICSVIEW
int x = pos.x(), y = pos.y();
const QWidget *w = this;
while (w) {
QWindow *window = w->windowHandle();
if (window && canMapPosition(window))
return window->mapToGlobal(QPoint(x, y));
x += w->data->crect.x();
y += w->data->crect.y();
w = w->isWindow() ? 0 : w->parentWidget();
}
return QPoint(x, y);
const MapToGlobalTransformResult t = mapToGlobalTransform(this);
const QPoint g = t.transform.map(pos);
return t.window ? t.window->mapToGlobal(g) : g;
}
/*!
@ -12392,29 +12419,9 @@ QPoint QWidget::mapToGlobal(const QPoint &pos) const
*/
QPoint QWidget::mapFromGlobal(const QPoint &pos) const
{
#ifndef QT_NO_GRAPHICSVIEW
Q_D(const QWidget);
if (d->extra && d->extra->proxyWidget && d->extra->proxyWidget->scene()) {
const QList <QGraphicsView *> views = d->extra->proxyWidget->scene()->views();
if (!views.isEmpty()) {
const QPoint viewPortPos = views.first()->viewport()->mapFromGlobal(pos);
const QPointF scenePos = views.first()->mapToScene(viewPortPos);
return d->extra->proxyWidget->mapFromScene(scenePos).toPoint();
}
}
#endif // !QT_NO_GRAPHICSVIEW
int x = pos.x(), y = pos.y();
const QWidget *w = this;
while (w) {
QWindow *window = w->windowHandle();
if (window && canMapPosition(window))
return window->mapFromGlobal(QPoint(x, y));
x -= w->data->crect.x();
y -= w->data->crect.y();
w = w->isWindow() ? 0 : w->parentWidget();
}
return QPoint(x, y);
const MapToGlobalTransformResult t = mapToGlobalTransform(this);
const QPoint windowLocal = t.window ? t.window->mapFromGlobal(pos) : pos;
return t.transform.inverted().map(windowLocal);
}
QWidget *qt_pressGrab = 0;

View File

@ -3682,16 +3682,19 @@ static QByteArray msgPointMismatch(const QPoint &actual, const QPoint &expected)
void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135
{
const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
const QSize size = availableGeometry.size() / 5;
const QSize size = availableGeometry.size() / 4;
QGraphicsScene scene;
QGraphicsView view(&scene);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setTransform(QTransform::fromScale(2, 2)); // QTBUG-50136, use transform.
view.setWindowTitle(QTest::currentTestFunction());
view.resize(size);
view.move(availableGeometry.bottomRight() - QPoint(size.width(), size.height()) - QPoint(100, 100));
QWidget *embeddedWidget = new QWidget;
embeddedWidget->setFixedSize(size / 2);
QWidget *childWidget = new QWidget(embeddedWidget);
QWidget *embeddedWidget = new QGroupBox(QLatin1String("Embedded"));
embeddedWidget->setStyleSheet(QLatin1String("background-color: \"yellow\"; "));
embeddedWidget->setFixedSize((size - QSize(10, 10)) / 2);
QWidget *childWidget = new QGroupBox(QLatin1String("Child"), embeddedWidget);
childWidget->setStyleSheet(QLatin1String("background-color: \"red\"; "));
childWidget->resize(embeddedWidget->size() / 2);
childWidget->move(embeddedWidget->width() / 4, embeddedWidget->height() / 4); // center in embeddedWidget
@ -3705,18 +3708,16 @@ void tst_QGraphicsProxyWidget::mapToGlobal() // QTBUG-41135
// This should be equivalent to the view center give or take rounding
// errors due to odd window margins
const QPoint viewCenter = view.geometry().center();
QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= 2,
QVERIFY2((viewCenter - embeddedCenterGlobal).manhattanLength() <= 3,
msgPointMismatch(embeddedCenterGlobal, viewCenter).constData());
// Same test with child centered on embeddedWidget. The correct
// mapping is not implemented yet, but at least make sure
// Same test with child centered on embeddedWidget. Also make sure
// the roundtrip maptoGlobal()/mapFromGlobal() returns the same
// point since that is important for mouse event handling (QTBUG-50030,
// QTBUG-50136).
const QPoint childCenter = childWidget->rect().center();
const QPoint childCenterGlobal = childWidget->mapToGlobal(childCenter);
QCOMPARE(childWidget->mapFromGlobal(childCenterGlobal), childCenter);
QEXPECT_FAIL("", "Not implemented for child widgets of embedded widgets", Continue);
QVERIFY2((viewCenter - childCenterGlobal).manhattanLength() <= 4,
msgPointMismatch(childCenterGlobal, viewCenter).constData());
}