From 931e67f5433468ea43dd2dcb03cdbd58936da2fd Mon Sep 17 00:00:00 2001 From: Sze Howe Koh Date: Tue, 12 Mar 2024 16:45:43 +0800 Subject: [PATCH] Popup/Drawer: Reposition based on Overlay geometry instead of Window geometry Calculations relating to the top and left edges are based on Overlay geometry, but calculations relating to the bottom and right edges are currently based on Window geometry instead. This patch makes all calculations based on Overlay geometry, for consistency. The single new auto-test covers both code paths modified by this patch, QQuickDrawerPositioner::reposition() and QQuickPopupPositioner::reposition() Change-Id: I6cf9bcc39985e7c9c325ba6c47198fb20215be4b Fixes: QTBUG-122963 Pick-to: 6.7 6.8 Reviewed-by: Mitch Curtis --- src/quicktemplates/qquickdrawer.cpp | 11 +++-- src/quicktemplates/qquickpopuppositioner.cpp | 19 ++++++-- .../qquickdrawer/data/rotate.qml | 40 ++++++++++++++++ .../qquickdrawer/tst_qquickdrawer.cpp | 48 +++++++++++++++++++ 4 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 tests/auto/quickcontrols/qquickdrawer/data/rotate.qml diff --git a/src/quicktemplates/qquickdrawer.cpp b/src/quicktemplates/qquickdrawer.cpp index b3d8ef3fc8..9eec4bd8a3 100644 --- a/src/quicktemplates/qquickdrawer.cpp +++ b/src/quicktemplates/qquickdrawer.cpp @@ -203,8 +203,11 @@ void QQuickDrawerPositioner::reposition() return; QQuickDrawer *drawer = static_cast(popup()); - QQuickWindow *window = drawer->window(); - if (!window) + + // The overlay is assumed to fully cover the window's contents, although the overlay's geometry + // might not always equal the window's geometry (for example, if the window's contents are rotated). + QQuickOverlay *overlay = QQuickOverlay::overlay(drawer->window()); + if (!overlay) return; const qreal position = drawer->position(); @@ -214,13 +217,13 @@ void QQuickDrawerPositioner::reposition() popupItem->setX((position - 1.0) * popupItem->width()); break; case Qt::RightEdge: - popupItem->setX(window->width() - position * popupItem->width()); + popupItem->setX(overlay->width() - position * popupItem->width()); break; case Qt::TopEdge: popupItem->setY((position - 1.0) * popupItem->height()); break; case Qt::BottomEdge: - popupItem->setY(window->height() - position * popupItem->height()); + popupItem->setY(overlay->height() - position * popupItem->height()); break; } diff --git a/src/quicktemplates/qquickpopuppositioner.cpp b/src/quicktemplates/qquickpopuppositioner.cpp index 08b6e9d358..4826cbabe5 100644 --- a/src/quicktemplates/qquickpopuppositioner.cpp +++ b/src/quicktemplates/qquickpopuppositioner.cpp @@ -153,12 +153,25 @@ void QQuickPopupPositioner::reposition() rect.moveTopLeft(m_parentItem->mapToItem(popupItem->parentItem(), rect.topLeft())); } - if (p->window) { + // The overlay is assumed to fully cover the window's contents, although the overlay's geometry + // might not always equal the window's geometry (for example, if the window's contents are rotated). + QQuickOverlay *overlay = QQuickOverlay::overlay(p->window); + if (overlay) { + qreal boundsWidth = overlay->width(); + qreal boundsHeight = overlay->height(); + + // QTBUG-126843: On some platforms, the overlay's geometry is not yet available at the instant + // when Component.completed() is emitted. Fall back to the window's geometry for this edge case. + if (Q_UNLIKELY(boundsWidth <= 0)) { + boundsWidth = p->window->width(); + boundsHeight = p->window->height(); + } + const QMarginsF margins = p->getMargins(); QRectF bounds(qMax(0.0, margins.left()), qMax(0.0, margins.top()), - p->window->width() - qMax(0.0, margins.left()) - qMax(0.0, margins.right()), - p->window->height() - qMax(0.0, margins.top()) - qMax(0.0, margins.bottom())); + boundsWidth - qMax(0.0, margins.left()) - qMax(0.0, margins.right()), + boundsHeight - qMax(0.0, margins.top()) - qMax(0.0, margins.bottom())); // if the popup doesn't fit horizontally inside the window, try flipping it around (left <-> right) if (p->allowHorizontalFlip && (rect.left() < bounds.left() || rect.right() > bounds.right())) { diff --git a/tests/auto/quickcontrols/qquickdrawer/data/rotate.qml b/tests/auto/quickcontrols/qquickdrawer/data/rotate.qml new file mode 100644 index 0000000000..25595f92cf --- /dev/null +++ b/tests/auto/quickcontrols/qquickdrawer/data/rotate.qml @@ -0,0 +1,40 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtQuick.Controls + +ApplicationWindow { + id: window + width: 600 + height: 400 // We want width != height to test rotated geometry + + // The derivation of this anchor setup is documented at QTBUG-123565 + background: Item { + width: window.rotated ? window.height : window.width + height: window.rotated ? window.width : window.height + anchors.centerIn: parent + } + Overlay.overlay.anchors.fill: background + contentItem.anchors.fill: background + contentItem.parent.rotation: rotated ? 90 : 0 + + property bool rotated: false + property int drawerSize: 100 + property alias drawer_LR: drawer_LR + property alias drawer_TB: drawer_TB + + Drawer { + id: drawer_LR + edge: Qt.LeftEdge + width: window.drawerSize + height: parent.height + } + Drawer { + id: drawer_TB + edge: Qt.TopEdge + width: parent.width + height: window.drawerSize + } +} + diff --git a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp index d16bc7790f..666d2619d2 100644 --- a/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp +++ b/tests/auto/quickcontrols/qquickdrawer/tst_qquickdrawer.cpp @@ -56,6 +56,7 @@ private slots: void dragMargin(); void reposition(); + void rotate(); void header(); void dragHandlerInteraction(); @@ -490,6 +491,53 @@ void tst_QQuickDrawer::reposition() QTRY_COMPARE(popupItem2->x(), 0.0); } +void tst_QQuickDrawer::rotate() +{ + QQuickControlsApplicationHelper helper(this, u"rotate.qml"_s); + QVERIFY2(helper.ready, helper.failureMessage()); + + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickDrawer *drawer_LR = window->property("drawer_LR").value(); + QVERIFY(drawer_LR); + QQuickItem *popupItem_LR = drawer_LR->popupItem(); + QVERIFY(popupItem_LR); + + QQuickDrawer *drawer_TB = window->property("drawer_TB").value(); + QVERIFY(drawer_TB); + QQuickItem *popupItem_TB = drawer_TB->popupItem(); + QVERIFY(popupItem_TB); + + drawer_LR->open(); + drawer_TB->open(); + + const int drawerSize = window->property("drawerSize").toInt(); + int virtualWidth = window->width(); + int virtualHeight = window->height(); + + // First iteration tests an unrotated window; 2nd iteration repeats the test with a rotated window + for (int i = 0; i < 2; ++i) { + drawer_LR->setEdge(Qt::LeftEdge); + QTRY_COMPARE(geometry(popupItem_LR), QRectF(0, 0, drawerSize, virtualHeight)); + + drawer_TB->setEdge(Qt::TopEdge); + QTRY_COMPARE(geometry(popupItem_TB), QRectF(0, 0, virtualWidth, drawerSize)); + + drawer_LR->setEdge(Qt::RightEdge); + QTRY_COMPARE(geometry(popupItem_LR), QRectF(virtualWidth - drawerSize, 0, drawerSize, virtualHeight)); + + drawer_TB->setEdge(Qt::BottomEdge); + QTRY_COMPARE(geometry(popupItem_TB), QRectF(0, virtualHeight - drawerSize, virtualWidth, drawerSize)); + + if (i == 0) { + window->setProperty("rotated", true); + std::swap(virtualWidth, virtualHeight); + } + } +} + void tst_QQuickDrawer::header() { QQuickControlsApplicationHelper helper(this, QStringLiteral("header.qml"));