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 <mitch.curtis@qt.io>
This commit is contained in:
Sze Howe Koh 2024-03-12 16:45:43 +08:00
parent fdac10eb0d
commit 931e67f543
4 changed files with 111 additions and 7 deletions

View File

@ -203,8 +203,11 @@ void QQuickDrawerPositioner::reposition()
return;
QQuickDrawer *drawer = static_cast<QQuickDrawer*>(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;
}

View File

@ -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<qreal>(0.0, margins.left()),
qMax<qreal>(0.0, margins.top()),
p->window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()),
p->window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom()));
boundsWidth - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()),
boundsHeight - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(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())) {

View File

@ -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
}
}

View File

@ -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<QQuickDrawer*>();
QVERIFY(drawer_LR);
QQuickItem *popupItem_LR = drawer_LR->popupItem();
QVERIFY(popupItem_LR);
QQuickDrawer *drawer_TB = window->property("drawer_TB").value<QQuickDrawer*>();
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"));