From 5d612a72ad6834a54a99fa6e23623ea6afa7afb8 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 29 Oct 2024 10:21:04 +0100 Subject: [PATCH] QWidget::mapTo/FromGlobal(): Fix transformation in case of QGraphicsItem::ItemIgnoresTransformations Extract a helper returning the transform from QGraphicsViewPrivate::mapToViewRect() and use that. Fixes: QTBUG-128913 Change-Id: Idc31f653c23cd7d0e5bbb8af560f010f01ac4d4b Reviewed-by: Axel Spoerl (cherry picked from commit deb4e08c1212aa3d43f62f9e7211bf69d3be0ada) Reviewed-by: Qt Cherry-pick Bot --- src/widgets/graphicsview/qgraphicsview.cpp | 27 +++++----- src/widgets/graphicsview/qgraphicsview_p.h | 1 + src/widgets/kernel/qwidget.cpp | 5 +- .../tst_qgraphicsproxywidget.cpp | 50 +++++++++++++++++++ 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/widgets/graphicsview/qgraphicsview.cpp b/src/widgets/graphicsview/qgraphicsview.cpp index 9505e2529aa..deb647a9729 100644 --- a/src/widgets/graphicsview/qgraphicsview.cpp +++ b/src/widgets/graphicsview/qgraphicsview.cpp @@ -885,16 +885,14 @@ void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEven /*! \internal */ -QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const +QTransform QGraphicsViewPrivate::mapToViewTransform(const QGraphicsItem *item) const { Q_Q(const QGraphicsView); if (dirtyScroll) const_cast(this)->updateScroll(); - if (item->d_ptr->itemIsUntransformable()) { - QTransform itv = item->deviceTransform(q->viewportTransform()); - return itv.mapRect(rect).toAlignedRect(); - } + if (item->d_ptr->itemIsUntransformable()) + return item->deviceTransform(q->viewportTransform()); // Translate-only // COMBINE @@ -908,21 +906,20 @@ QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRect offset += itemd->pos; } while ((parentItem = itemd->parent)); - QRectF baseRect = rect.translated(offset.x(), offset.y()); + QTransform move = QTransform::fromTranslate(offset.x(), offset.y()); if (!parentItem) { - if (identityMatrix) { - baseRect.translate(-scrollX, -scrollY); - return baseRect.toAlignedRect(); - } - return matrix.mapRect(baseRect).translated(-scrollX, -scrollY).toAlignedRect(); + move.translate(-scrollX, -scrollY); + return identityMatrix ? move : matrix * move; } - QTransform tr = parentItem->sceneTransform(); if (!identityMatrix) tr *= matrix; - QRectF r = tr.mapRect(baseRect); - r.translate(-scrollX, -scrollY); - return r.toAlignedRect(); + return move * tr * QTransform::fromTranslate(-scrollX, -scrollY); +} + +QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const +{ + return mapToViewTransform(item).mapRect(rect).toAlignedRect(); } /*! diff --git a/src/widgets/graphicsview/qgraphicsview_p.h b/src/widgets/graphicsview/qgraphicsview_p.h index 7f1682becac..0a4699c9ca5 100644 --- a/src/widgets/graphicsview/qgraphicsview_p.h +++ b/src/widgets/graphicsview/qgraphicsview_p.h @@ -139,6 +139,7 @@ public: void populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest, QDropEvent *source); + QTransform mapToViewTransform(const QGraphicsItem *item) const; QRect mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const; QRegion mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const; QRegion dirtyRegion; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 881c8ba9a4a..3c718652f3a 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -60,6 +60,7 @@ #include "QtWidgets/qgraphicsproxywidget.h" #include "QtWidgets/qgraphicsscene.h" #include "private/qgraphicsproxywidget_p.h" +#include "private/qgraphicsview_p.h" #endif #include "QtWidgets/qabstractscrollarea.h" #include "private/qabstractscrollarea_p.h" @@ -12644,8 +12645,8 @@ static MapToGlobalTransformResult mapToGlobalTransform(const QWidget *w) if (const QGraphicsScene *scene = qgpw->scene()) { const QList views = scene->views(); if (!views.isEmpty()) { - result.transform *= qgpw->sceneTransform(); - result.transform *= views.first()->viewportTransform(); + auto *viewP = static_cast(qt_widget_private(views.constFirst())); + result.transform *= viewP->mapToViewTransform(qgpw); w = views.first()->viewport(); } } diff --git a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index c01a80b0530..3362acac9b5 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -135,6 +135,7 @@ private slots: void QTBUG_6986_sendMouseEventToAlienWidget(); void mapToGlobal(); void mapToGlobalWithoutScene(); + void mapToGlobalIgnoreTranformation(); void QTBUG_43780_visibility(); #if QT_CONFIG(wheelevent) void wheelEventPropagation(); @@ -3552,6 +3553,55 @@ void tst_QGraphicsProxyWidget::mapToGlobalWithoutScene() // QTBUG-44509 QCOMPARE(embeddedWidget->mapFromGlobal(globalPos), localPos); } +// QTBUG-128913, QGraphicsProxyWidget with ItemIgnoresTransformations +void tst_QGraphicsProxyWidget::mapToGlobalIgnoreTranformation() +{ + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); + const QSize size = availableGeometry.size() / 2; + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowTitle(QLatin1StringView(QTest::currentTestFunction())); + view.setAlignment(Qt::AlignLeft | Qt::AlignTop); + view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view.setTransform(QTransform::fromScale(2, 2)); + view.resize(size); + view.move(availableGeometry.bottomRight() - QPoint(size.width(), size.height()) + - QPoint(100, 100)); + + static constexpr int labelWidth = 200; + static constexpr int labelHeight = 50; + auto *transforming = new QGraphicsProxyWidget(); + auto *transformingLabel = new QLabel("Transforming"_L1); + transformingLabel->resize(labelWidth, labelHeight); + transforming->setWidget(transformingLabel); + transforming->setPos(0, 0); + scene.addItem(transforming); + + auto *nonTransforming = new QGraphicsProxyWidget(); + nonTransforming->setFlag(QGraphicsItem::ItemIgnoresTransformations); + auto *nonTransformingLabel = new QLabel("NonTransforming"_L1); + nonTransformingLabel->resize(labelWidth, labelHeight); + nonTransforming->setWidget(nonTransformingLabel); + nonTransforming->setPos(labelWidth, 0); + scene.addItem(nonTransforming); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + const QPoint labelCenter{ labelWidth / 2, labelHeight / 2 }; + const QPoint topPos = view.geometry().topLeft() + view.viewport()->geometry().topLeft(); + const QPoint transformingGlobal = transformingLabel->mapToGlobal(labelCenter); + const QPoint nonTransformingGlobal = nonTransformingLabel->mapToGlobal(labelCenter); + + // Center of label at 0,0 scaled by 2 should match size + QCOMPARE(transformingGlobal - topPos, QPoint(labelWidth, labelHeight)); + + // Center of non-transforming label at 200 (scaled by 2), 0 + QCOMPARE(nonTransformingGlobal - topPos, + QPoint(labelWidth * 2 + labelWidth / 2, labelHeight / 2)); +} + // QTBUG_43780: Embedded widgets have isWindow()==true but showing them should not // trigger the top-level widget code path of show() that closes all popups // (for example combo popups).