Resize offscreen window when QQuickWidget is resized
In a typical Qt Quick application, when a window is resized, the contentItem of that window is resized with it, and then the root item. QQuickOverlay in qtquickcontrols2 listens to size changes in the contentItem (QQuickRootItem) via addItemChangeListener(), as a cheap way (e.g. no signals) of knowing when to resize background dimming effects. It resizes the dimmer item to the size of the window. The first problem with QQuickWidget is that it only ever resizes the root item when using the SizeRootObjectToView resize mode, and not the contentItem. The second problem is that the root item is resized (via updateSize()) before the window itself even has a size (which happens in QQuickWidget::createFramebufferObject() via the call to d->offscreenWindow->setGeometry()). To demonstrate the second problem in detail, consider the following widget hierarchy (written in everybody's favorite language: QML): QMainWindow { QQuickWidget { QQuickWindow { // QQuickWidgetPrivate::offscreenWindow QQuickRootItem { // QQuickWindowPrivate::contentItem Page {} // QQuickWidgetPrivate::root } } } } The QMainWindow starts off as 200x200. When the window is resized, QQuickWidget::resizeEvent() is called. The first thing it does is call updateSize(), which in the case of SizeRootObjectToView, resizes the root item to 300x300. This causes QQuickOverlayPrivate::itemGeometryChanged() to be called, and the dimmers are resized to the size of the window, but the window still has its 200x200 size, as it is only updated later, when QQuickWidget::createFramebufferObject() is called. This patch fixes these issues by ensuring that contentItem and the window itself are resized along with the root item. As to why such manual intervention is necessary: from what I can see, it is because it's an "offscreen" window. This means that QWindowPrivate::platformWindow is null, and setGeometry() takes a different path that presumably results in no QResizeEvent being sent to the QQuickWindow. As QQuickWindow relies on resizeEvent() being called to resize its contentItem, the contentItem is never resized. With a typical Qt Quick application, all of this works as expected. Change-Id: I7401aa7a9b209096183416ab53014f67cceccbe4 Fixes: QTBUG-78323 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io> Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
parent
7bef203438
commit
7be805d155
|
@ -107,6 +107,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e)
|
|||
renderControl = new QQuickWidgetRenderControl(q);
|
||||
offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(),renderControl);
|
||||
offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
|
||||
offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow"));
|
||||
// Do not call create() on offscreenWindow.
|
||||
|
||||
// Check if the Software Adaptation is being used
|
||||
|
@ -802,15 +803,29 @@ void QQuickWidgetPrivate::updateSize()
|
|||
q->updateGeometry();
|
||||
}
|
||||
} else if (resizeMode == QQuickWidget::SizeRootObjectToView) {
|
||||
bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
|
||||
bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
|
||||
const bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
|
||||
const bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
|
||||
|
||||
if (needToUpdateWidth && needToUpdateHeight)
|
||||
root->setSize(QSizeF(q->width(), q->height()));
|
||||
else if (needToUpdateWidth)
|
||||
root->setWidth(q->width());
|
||||
else if (needToUpdateHeight)
|
||||
root->setHeight(q->height());
|
||||
if (needToUpdateWidth && needToUpdateHeight) {
|
||||
// Make sure that we have realistic sizing behavior by following
|
||||
// what on-screen windows would do and resize everything, not just
|
||||
// the root item. We do this because other types may be relying on
|
||||
// us to behave correctly.
|
||||
const QSizeF newSize(q->width(), q->height());
|
||||
offscreenWindow->resize(newSize.toSize());
|
||||
offscreenWindow->contentItem()->setSize(newSize);
|
||||
root->setSize(newSize);
|
||||
} else if (needToUpdateWidth) {
|
||||
const int newWidth = q->width();
|
||||
offscreenWindow->setWidth(newWidth);
|
||||
offscreenWindow->contentItem()->setWidth(newWidth);
|
||||
root->setWidth(newWidth);
|
||||
} else if (needToUpdateHeight) {
|
||||
const int newHeight = q->height();
|
||||
offscreenWindow->setHeight(newHeight);
|
||||
offscreenWindow->contentItem()->setHeight(newHeight);
|
||||
root->setHeight(newHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import Test 1.0
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property alias overlay: overlay
|
||||
|
||||
Overlay {
|
||||
id: overlay
|
||||
// Parent it to the contentItem of the underlying QQuickWindow.
|
||||
parent: root.parent
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
#include <QtQml/qqmlcontext.h>
|
||||
#include <QtQuick/qquickview.h>
|
||||
#include <QtQuick/qquickitem.h>
|
||||
#include <QtQuick/private/qquickitem_p.h>
|
||||
#include "../../shared/util.h"
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QScreen>
|
||||
|
@ -142,6 +143,7 @@ private slots:
|
|||
void synthMouseFromTouch_data();
|
||||
void synthMouseFromTouch();
|
||||
void tabKey();
|
||||
void resizeOverlay();
|
||||
|
||||
private:
|
||||
QTouchDevice *device = QTest::createTouchDevice();
|
||||
|
@ -662,6 +664,74 @@ void tst_qquickwidget::tabKey()
|
|||
QVERIFY(middleItem->property("activeFocus").toBool());
|
||||
}
|
||||
|
||||
class Overlay : public QQuickItem, public QQuickItemChangeListener
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Overlay() = default;
|
||||
|
||||
~Overlay()
|
||||
{
|
||||
QQuickItemPrivate::get(parentItem())->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
|
||||
}
|
||||
|
||||
// componentCompleted() is too early to add the listener, as parentItem()
|
||||
// is still null by that stage, so we use this function instead.
|
||||
void startListening()
|
||||
{
|
||||
QQuickItemPrivate::get(parentItem())->addItemChangeListener(this, QQuickItemPrivate::Geometry);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &/*oldGeometry*/) override
|
||||
{
|
||||
auto window = QQuickItemPrivate::get(this)->window;
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
setSize(window->size());
|
||||
}
|
||||
};
|
||||
|
||||
// Test that an item that resizes itself based on the window size can use a
|
||||
// Geometry item change listener to respond to changes in size. This is a
|
||||
// simplified test to mimic a use case involving Overlay from Qt Quick Controls 2.
|
||||
void tst_qquickwidget::resizeOverlay()
|
||||
{
|
||||
QWidget widget;
|
||||
auto contentVerticalLayout = new QVBoxLayout(&widget);
|
||||
contentVerticalLayout->setMargin(0);
|
||||
|
||||
qmlRegisterType<Overlay>("Test", 1, 0, "Overlay");
|
||||
|
||||
auto quickWidget = new QQuickWidget(testFileUrl("resizeOverlay.qml"), &widget);
|
||||
QCOMPARE(quickWidget->status(), QQuickWidget::Ready);
|
||||
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
contentVerticalLayout->addWidget(quickWidget);
|
||||
|
||||
auto rootItem = qobject_cast<QQuickItem*>(quickWidget->rootObject());
|
||||
QVERIFY(rootItem);
|
||||
|
||||
auto overlay = rootItem->property("overlay").value<Overlay*>();
|
||||
QVERIFY(overlay);
|
||||
QVERIFY(overlay->parentItem());
|
||||
overlay->startListening();
|
||||
|
||||
widget.resize(200, 200);
|
||||
widget.show();
|
||||
QCOMPARE(rootItem->width(), 200);
|
||||
QCOMPARE(rootItem->height(), 200);
|
||||
QCOMPARE(overlay->width(), rootItem->width());
|
||||
QCOMPARE(overlay->height(), rootItem->height());
|
||||
|
||||
widget.resize(300, 300);
|
||||
QCOMPARE(rootItem->width(), 300);
|
||||
QCOMPARE(rootItem->height(), 300);
|
||||
QCOMPARE(overlay->width(), rootItem->width());
|
||||
QCOMPARE(overlay->height(), rootItem->height());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qquickwidget)
|
||||
|
||||
#include "tst_qquickwidget.moc"
|
||||
|
|
Loading…
Reference in New Issue