Remove resize guards from qml tool

Resize guards were prohibiting the initialization of the top level
item size to the window size in a qml script when executed with
the qml tool. This is usually fine because the window size is set
to the item size. However, if this is not possible (minimum window
size from the system, no explicit size on the item), the item has
a different size than the window. The item would only be adapted to
the window size after resizing the window.

This patch removes the resize guards to make the item always fill
the window on startup. The resize guard should hopefully not be needed;
the QQuickItem setters for width and height return early if it's
unchanged; QWindow::resize() does not emit changed signals if there
is not yet a platform window and width or height is unchanged;
and we hope all the overrides of QPlatformWindow::setGeometry()
will avoid making unnecessary changes. However the guards were added
in 8d9a7e47aa and I don't remember
exactly why.

Adds the test tst_qml::itemAndWindowGeometry to check combinations of
--config, --qwindowgeometry and qml files whose root item has or doesn't
have its own dimensions, or changes its dimensions after being shown.

Done-with: Matthias Rauter <matthias.rauter@qt.io>
Pick-to: 6.5
Fixes: QTBUG-114068
Fixes: QTBUG-116753
Change-Id: Ib972e0bfc25809441d378c53dabb60653314f5a6
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(cherry picked from commit 05e0dc2a88)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Shawn Rutledge 2023-09-05 22:00:43 +02:00 committed by Qt Cherry-pick Bot
parent e070c48ad4
commit 410c2a4440
6 changed files with 194 additions and 5 deletions

View File

@ -15,4 +15,6 @@ qt_internal_add_test(tst_qml
QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
LIBRARIES
Qt::QuickTestUtilsPrivate
Qt::Gui
Qt::GuiPrivate
)

View File

@ -0,0 +1,28 @@
import QtQuick
Rectangle {
id: rect
color: "green"
Timer {
id: exitTimer
running: false
onTriggered: Qt.quit()
}
Timer {
id: resizeTimer
running: false
onTriggered: {
rect.width = 100
rect.height = 50
exitTimer.start()
}
}
Window.onHeightChanged: {
if (rect.Window.width > 0)
console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height)
resizeTimer.start()
}
}

View File

@ -0,0 +1,19 @@
import QtQuick
Rectangle {
id: rect
color: "blue"
width: 200; height: 150
Timer {
id: exitTimer
running: false
onTriggered: Qt.quit()
}
Window.onHeightChanged: {
if (rect.Window.width > 0)
console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height)
exitTimer.restart()
}
}

View File

@ -0,0 +1,18 @@
import QtQuick
Rectangle {
id: rect
color: "green"
Timer {
id: exitTimer
running: false
onTriggered: Qt.quit()
}
Window.onHeightChanged: {
if (rect.Window.width > 0)
console.info("window", rect.Window.width, rect.Window.height, "content", rect.width, rect.height)
exitTimer.restart()
}
}

View File

@ -4,8 +4,13 @@
#include <QtTest/qtest.h>
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qprocess.h>
#include <QtCore/qloggingcategory.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
Q_LOGGING_CATEGORY(lcQml, "qt.qml.tests");
class tst_qml : public QQmlDataTest
{
Q_OBJECT
@ -15,6 +20,8 @@ public:
private slots:
void initTestCase() override;
void nonWindow();
void itemAndWindowGeometry_data();
void itemAndWindowGeometry();
private:
QString qmlPath;
@ -42,6 +49,124 @@ void tst_qml::nonWindow()
QCOMPARE(qml.exitCode(), 0); // Should not exit with code 2
}
void tst_qml::itemAndWindowGeometry_data()
{
QTest::addColumn<QString>("config");
QTest::addColumn<QString>("geometry");
QTest::addColumn<QString>("qmlfile");
QTest::addColumn<QSize>("expectedWindowSize");
QTest::addColumn<QSize>("expectedContentSize");
const QString none; // empty string
auto sizeOrInvalid = [](int w, int h) {
static const bool wm = QGuiApplicationPrivate::platformIntegration()->
hasCapability(QPlatformIntegration::WindowManagement);
return wm ? QSize(w, h) : QSize();
};
QTest::newRow("default: unsized")
<< none << none << "unsizedItem.qml"
<< QSize() << QSize(); // default size depends on window system
QTest::newRow("default: unsized with geometry")
<< none << "100x100+50+50" << "unsizedItem.qml"
<< sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100);
QTest::newRow("resizeToItem: unsized")
<< "resizeToItem" << none << "unsizedItem.qml"
<< QSize() << QSize(0, 0);
QTest::newRow("resizeToItem: unsized with geometry")
<< "resizeToItem" << "100x100+50+50" << "unsizedItem.qml"
<< sizeOrInvalid(100, 100) << QSize(0, 0);
QTest::newRow("default: sized")
<< none << none << "sizedItem.qml"
<< QSize() << QSize();
QTest::newRow("default: sized with geometry")
<< none << "100x100+50+50" << "sizedItem.qml"
<< sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100);
QTest::newRow("resizeToItem: sized")
<< "resizeToItem" << none << "sizedItem.qml"
<< sizeOrInvalid(200, 150) << sizeOrInvalid(200, 150);
QTest::newRow("resizeToItem: sized with geometry")
<< "resizeToItem" << "320x240+50+50" << "sizedItem.qml"
<< sizeOrInvalid(320, 240) << QSize(200, 150);
QTest::newRow("default: resizing")
<< none << none << "resizeItem.qml"
<< QSize() << QSize();
QTest::newRow("default: resizing with geometry")
<< none << "100x100+50+50" << "resizeItem.qml"
<< sizeOrInvalid(100, 100) << sizeOrInvalid(100, 100);
QTest::newRow("resizeToItem: resizing")
<< "resizeToItem" << none << "resizeItem.qml"
<< sizeOrInvalid(100, 50) << sizeOrInvalid(100, 50);
QTest::newRow("resizeToItem: resizing with geometry")
<< "resizeToItem" << "320x240+50+50" << "resizeItem.qml"
<< sizeOrInvalid(100, 50) << sizeOrInvalid(100, 50);
}
/*
- A root Item will get put into a Window depending on config (implementations in
tools/qml/ResizeItemToWindow.qml and ResizeWindowToItem.qml).
- The window system will enforce a minimum size.
- In the default configuration, the root Item should then get resized to fit
(QTBUG-114068 / QTBUG-116753).
- In resizeToItem configuration, if the item width/height are not set, the window would
try to be 0x0, but the window system won't allow it.
- This also tests the `--qwindowgeometry` argument: with the default config, the
item should be resized to fit, but not with `-c resizeToItem`.
*/
void tst_qml::itemAndWindowGeometry()
{
#ifdef Q_OS_WIN
QSKIP("console.info does not go to stderr on Windows.");
#endif
QFETCH(QString, config);
QFETCH(QString, geometry);
QFETCH(QString, qmlfile);
QFETCH(QSize, expectedWindowSize);
QFETCH(QSize, expectedContentSize);
QStringList args;
if (!config.isEmpty())
args << "-c" << config;
if (!geometry.isEmpty())
args << "--qwindowgeometry" << geometry;
args << testFile(qmlfile);
QProcess qml;
qml.start(qmlPath, args);
QVERIFY(qml.waitForFinished());
QCOMPARE(qml.exitStatus(), QProcess::NormalExit);
const QByteArray output = qml.readAllStandardError();
const auto sizeLineIndex = output.lastIndexOf("window");
QCOMPARE_GE(sizeLineIndex, 0);
const auto newlineIndex = output.indexOf('\n', sizeLineIndex);
QCOMPARE_GT(newlineIndex, sizeLineIndex);
// expect a line like "window 120 120 content 120 120"
const auto sizes = output.sliced(sizeLineIndex, newlineIndex - sizeLineIndex).split(' ');
QCOMPARE_GE(sizes.size(), 6);
QCOMPARE(sizes[0], "window");
QCOMPARE(sizes[3], "content");
const QSize windowSize(sizes[1].toInt(), sizes[2].toInt());
const QSize contentSize(sizes[4].toInt(), sizes[5].toInt());
qCDebug(lcQml) << sizes
<< "window" << windowSize << "expect" << expectedWindowSize
<< "content" << contentSize << "expect" << expectedContentSize;
QVERIFY(!windowSize.isEmpty());
if (config != "resizeToItem") {
// default config:
// ResizeItemToWindow.qml should have resized the item to its window
QCOMPARE(contentSize, windowSize);
}
// windowSize can be off-by-one on hidpi (e.g. QT_SCALE_FACTOR=2 on xcb);
// perhaps that's a bug somewhere, but so far we aren't testing hidpi on CI
if (expectedWindowSize.isValid())
QCOMPARE(windowSize, expectedWindowSize);
if (expectedContentSize.isValid())
QCOMPARE(contentSize, expectedContentSize);
}
QTEST_MAIN(tst_qml)
#include <tst_qml.moc>

View File

@ -5,19 +5,16 @@ import QtQuick 2.0
Window {
property Item containedObject: null
property bool __resizeGuard: false
onContainedObjectChanged: {
if (containedObject == undefined || containedObject == null) {
visible = false;
return;
}
__resizeGuard = true
width = containedObject.width;
height = containedObject.height;
containedObject.parent = contentItem;
visible = true;
__resizeGuard = false
}
onWidthChanged: if (!__resizeGuard && containedObject) containedObject.width = width
onHeightChanged: if (!__resizeGuard && containedObject) containedObject.height = height
onWidthChanged: if (containedObject) containedObject.width = width
onHeightChanged: if (containedObject) containedObject.height = height
}