aboutsummaryrefslogtreecommitdiffstats
path: root/src/quickwidgets/qquickwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quickwidgets/qquickwidget.cpp')
-rw-r--r--src/quickwidgets/qquickwidget.cpp867
1 files changed, 497 insertions, 370 deletions
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 8ddd2d76c7..c782dc74b1 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -1,44 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickwidget.h"
#include "qquickwidget_p.h"
+#include "qaccessiblequickwidgetfactory_p.h"
+#include <QtWidgets/private/qwidgetrepaintmanager_p.h>
#include "private/qquickwindow_p.h"
#include "private/qquickitem_p.h"
@@ -59,14 +25,6 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
-#if QT_CONFIG(opengl)
-#include <private/qopenglcontext_p.h>
-#include <private/qopenglextensions_p.h>
-#include <QOpenGLFramebufferObject>
-#include <QOpenGLContext>
-#include <QOpenGLFunctions>
-#include <QtOpenGL/qpa/qplatformbackingstoreopenglsupport.h>
-#endif
#include <QtGui/QPainter>
#include <QtQuick/QSGRendererInterface>
@@ -82,11 +40,29 @@
#include <QtQuick/qquickgraphicsdevice.h>
#include <QtQuick/qquickrendertarget.h>
+#include "private/qwidget_p.h"
+
+#if QT_CONFIG(graphicsview)
+#include <QtWidgets/qgraphicsscene.h>
+#include <QtWidgets/qgraphicsview.h>
+#endif
+
+#if QT_CONFIG(vulkan)
+#include <QtGui/private/qvulkandefaultinstance_p.h>
+#endif
+
QT_BEGIN_NAMESPACE
+QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
+:QQuickWindow(dd, control)
+{
+ setTitle(QString::fromLatin1("Offscreen"));
+ setObjectName(QString::fromLatin1("QQuickWidgetOffscreenWindow"));
+}
+
// override setVisble to prevent accidental offscreen window being created
// by base class.
-class QQuickOffcreenWindowPrivate: public QQuickWindowPrivate {
+class QQuickWidgetOffscreenWindowPrivate: public QQuickWindowPrivate {
public:
void setVisible(bool visible) override {
Q_Q(QWindow);
@@ -96,61 +72,155 @@ public:
}
};
+class QQuickWidgetRenderControlPrivate;
+
class QQuickWidgetRenderControl : public QQuickRenderControl
{
+ Q_DECLARE_PRIVATE(QQuickWidgetRenderControl)
public:
- QQuickWidgetRenderControl(QQuickWidget *quickwidget) : m_quickWidget(quickwidget) {}
- QWindow *renderWindow(QPoint *offset) override {
- if (offset)
- *offset = m_quickWidget->mapTo(m_quickWidget->window(), QPoint());
- return m_quickWidget->window()->windowHandle();
+ QQuickWidgetRenderControl(QQuickWidget *quickwidget);
+ QWindow *renderWindow(QPoint *offset) override;
+
+};
+
+class QQuickWidgetRenderControlPrivate : public QQuickRenderControlPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickWidgetRenderControl)
+ QQuickWidgetRenderControlPrivate(QQuickWidgetRenderControl *renderControl, QQuickWidget *qqw)
+ : QQuickRenderControlPrivate(renderControl)
+ , m_quickWidget(qqw)
+ {
+ }
+
+ bool isRenderWindow(const QWindow *w) override {
+#if QT_CONFIG(graphicsview)
+ QWidgetPrivate *widgetd = QWidgetPrivate::get(m_quickWidget);
+ auto *proxy = (widgetd && widgetd->extra) ? widgetd->extra->proxyWidget : nullptr;
+ auto *scene = proxy ? proxy->scene() : nullptr;
+ if (scene) {
+ for (const auto &view : scene->views()) {
+ if (view->window()->windowHandle() == w)
+ return true;
+ }
+ }
+
+ return m_quickWidget->window()->windowHandle() == w;
+#endif
}
-private:
QQuickWidget *m_quickWidget;
};
+QQuickWidgetRenderControl::QQuickWidgetRenderControl(QQuickWidget *quickWidget)
+ : QQuickRenderControl(*(new QQuickWidgetRenderControlPrivate(this, quickWidget)), nullptr)
+{
+}
+
+QWindow *QQuickWidgetRenderControl::renderWindow(QPoint *offset)
+{
+ Q_D(QQuickWidgetRenderControl);
+ if (offset)
+ *offset = d->m_quickWidget->mapTo(d->m_quickWidget->window(), QPoint());
+
+ QWindow *result = nullptr;
+#if QT_CONFIG(graphicsview)
+ QWidgetPrivate *widgetd = QWidgetPrivate::get(d->m_quickWidget);
+ if (widgetd->extra) {
+ if (auto proxy = widgetd->extra->proxyWidget) {
+ auto scene = proxy->scene();
+ if (scene) {
+ const auto views = scene->views();
+ if (!views.isEmpty()) {
+ // Get the first QGV containing the proxy. Not ideal, but the callers
+ // of this function aren't prepared to handle more than one render window.
+ auto candidateView = views.first();
+ result = candidateView->window()->windowHandle();
+ }
+ }
+ }
+ }
+#endif
+ if (!result)
+ result = d->m_quickWidget->window()->windowHandle();
+
+ return result;
+}
+
void QQuickWidgetPrivate::initOffscreenWindow()
{
Q_Q(QQuickWidget);
- offscreenWindow = new QQuickWindow(*new QQuickOffcreenWindowPrivate(), renderControl);
- offscreenWindow->setTitle(QString::fromLatin1("Offscreen"));
- offscreenWindow->setObjectName(QString::fromLatin1("QQuickOffScreenWindow"));
+
+ ensureBackingScene();
+ offscreenWindow->setScreen(q->screen());
// Do not call create() on offscreenWindow.
QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject()));
QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject()));
QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged);
+
+#if QT_CONFIG(accessibility)
+ QAccessible::installFactory(&qAccessibleQuickWidgetFactory);
+#endif
}
-void QQuickWidgetPrivate::init(QQmlEngine* e)
+void QQuickWidgetPrivate::ensureBackingScene()
{
- Q_Q(QQuickWidget);
+ // This should initialize, if not already done, the absolute minimum set of
+ // mandatory backing resources, meaning the QQuickWindow and its
+ // QQuickRenderControl. This function may be called very early on upon
+ // construction, including before init() even.
- renderControl = new QQuickWidgetRenderControl(q);
- initOffscreenWindow();
+ Q_Q(QQuickWidget);
+ if (!renderControl)
+ renderControl = new QQuickWidgetRenderControl(q);
+ if (!offscreenWindow)
+ offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl);
// Check if the Software Adaptation is being used
auto sgRendererInterface = offscreenWindow->rendererInterface();
if (sgRendererInterface && sgRendererInterface->graphicsApi() == QSGRendererInterface::Software)
useSoftwareRenderer = true;
+}
+
+void QQuickWidgetPrivate::init(QQmlEngine* e)
+{
+ Q_Q(QQuickWidget);
+
+ initOffscreenWindow();
if (!useSoftwareRenderer) {
-#if QT_CONFIG(opengl)
- if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RasterGLSurface))
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering))
setRenderToTexture();
else
-#endif
qWarning("QQuickWidget is not supported on this platform.");
}
- if (QSGRhiSupport::instance()->rhiBackend() != QRhi::OpenGLES2)
- qWarning("QQuickWidget is only supported on OpenGL. Use QQuickWindow::setGraphicsApi() to override the default.");
-
engine = e;
if (!engine.isNull() && !engine.data()->incubationController())
engine.data()->setIncubationController(offscreenWindow->incubationController());
+ q->setMouseTracking(true);
+ q->setFocusPolicy(Qt::StrongFocus);
+#ifndef Q_OS_MACOS
+ /*
+ Usually, a QTouchEvent comes from a touchscreen, and we want those
+ touch events in Qt Quick. But on macOS, there are no touchscreens, and
+ WA_AcceptTouchEvents has a different meaning: QApplication::notify()
+ calls the native-integration function registertouchwindow() to change
+ NSView::allowedTouchTypes to include NSTouchTypeMaskIndirect when the
+ trackpad cursor enters the window, and removes that mask when the
+ cursor exits. In other words, WA_AcceptTouchEvents enables getting
+ discrete touchpoints from the trackpad. We rather prefer to get mouse,
+ wheel and native gesture events from the trackpad (because those
+ provide more of a "native feel"). The only exception is for
+ MultiPointTouchArea, and it takes care of that for itself. So don't
+ automatically set WA_AcceptTouchEvents on macOS. The user can still do
+ it, but we don't recommend it.
+ */
+ q->setAttribute(Qt::WA_AcceptTouchEvents);
+#endif
+
#if QT_CONFIG(quick_draganddrop)
q->setAcceptDrops(true);
#endif
@@ -171,38 +241,24 @@ void QQuickWidgetPrivate::ensureEngine() const
void QQuickWidgetPrivate::invalidateRenderControl()
{
-#if QT_CONFIG(opengl)
- if (!useSoftwareRenderer) {
- if (!context) // this is not an error, could be called before creating the context, or multiple times
- return;
-
- bool success = context->makeCurrent(offscreenSurface);
- if (!success) {
- qWarning("QQuickWidget::invalidateRenderControl could not make context current");
- return;
- }
+ if (!useSoftwareRenderer && rhi) {
+ // For the user's own OpenGL code connected to some QQuickWindow signals.
+ rhi->makeThreadLocalNativeContextCurrent();
}
-#endif
renderControl->invalidate();
-
- // Many things can happen inside the above invalidate() call, including a
- // change of current context. Restore if needed since some code will rely
- // on the fact that this function makes and leaves the context current.
-#if QT_CONFIG(opengl)
- if (!useSoftwareRenderer && context) {
- if (QOpenGLContext::currentContext() != context)
- context->makeCurrent(offscreenSurface);
- }
-#endif
}
void QQuickWidgetPrivate::handleWindowChange()
{
Q_Q(QQuickWidget);
- if (offscreenWindow->isPersistentSceneGraph() && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts))
+ if (offscreenWindow->isPersistentSceneGraph()
+ && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)
+ && rhiConfig().api() == QPlatformBackingStoreRhiConfig::OpenGL)
+ {
return;
+ }
// In case of !isPersistentSceneGraph or when we need a new context due to
// the need to share resources with the new window's context, we must both
@@ -210,7 +266,7 @@ void QQuickWidgetPrivate::handleWindowChange()
// must be recreated because its RHI will contain a dangling pointer to
// the context.
- delete offscreenWindow;
+ QScopedPointer<QQuickWindow> oldOffScreenWindow(offscreenWindow); // Do not delete before reparenting sgItem
offscreenWindow = nullptr;
delete renderControl;
@@ -220,22 +276,23 @@ void QQuickWidgetPrivate::handleWindowChange()
QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate()));
QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate()));
- execute();
- if (!useSoftwareRenderer)
- destroyContext();
+ if (!source.isEmpty())
+ execute();
+ else if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(root))
+ sgItem->setParentItem(offscreenWindow->contentItem());
}
QQuickWidgetPrivate::QQuickWidgetPrivate()
: root(nullptr)
, component(nullptr)
, offscreenWindow(nullptr)
- , offscreenSurface(nullptr)
, renderControl(nullptr)
-#if QT_CONFIG(opengl)
- , fbo(nullptr)
- , resolvedFbo(nullptr)
- , context(nullptr)
-#endif
+ , rhi(nullptr)
+ , outputTexture(nullptr)
+ , depthStencil(nullptr)
+ , msaaBuffer(nullptr)
+ , rt(nullptr)
+ , rtRp(nullptr)
, resizeMode(QQuickWidget::SizeViewToRootObject)
, initialSize(0,0)
, eventPending(false)
@@ -244,28 +301,18 @@ QQuickWidgetPrivate::QQuickWidgetPrivate()
, requestedSamples(0)
, useSoftwareRenderer(false)
, forceFullUpdate(false)
+ , deviceLost(false)
{
}
-QQuickWidgetPrivate::~QQuickWidgetPrivate()
+void QQuickWidgetPrivate::destroy()
{
+ Q_Q(QQuickWidget);
invalidateRenderControl();
-
- if (useSoftwareRenderer) {
- delete renderControl;
- delete offscreenWindow;
- } else {
-#if QT_CONFIG(opengl)
- // context and offscreenSurface are current at this stage, if the context was created.
- Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface));
- delete resolvedFbo;
- delete fbo;
- delete offscreenWindow;
- delete renderControl;
-
- destroyContext();
-#endif
- }
+ q->destroyFramebufferObject();
+ delete offscreenWindow;
+ delete renderControl;
+ offscreenRenderer.reset();
}
void QQuickWidgetPrivate::execute()
@@ -305,34 +352,37 @@ void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeom
void QQuickWidgetPrivate::render(bool needsSync)
{
+ Q_Q(QQuickWidget);
if (!useSoftwareRenderer) {
-#if QT_CONFIG(opengl)
- // createFramebufferObject() bails out when the size is empty. In this case
- // we cannot render either.
- if (!fbo)
- return;
-
- Q_ASSERT(context);
-
- bool current = context->makeCurrent(offscreenSurface);
-
- if (!current && !context->isValid()) {
- renderControl->invalidate();
- current = context->create() && context->makeCurrent(offscreenSurface);
- if (current) {
- offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context));
- renderControl->initialize();
- }
+ if (deviceLost) {
+ deviceLost = false;
+ initializeWithRhi();
+ q->createFramebufferObject();
}
- if (!current) {
- qWarning("QQuickWidget: Cannot render due to failing makeCurrent()");
+ if (!rhi) {
+ qWarning("QQuickWidget: Attempted to render scene with no rhi");
return;
}
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = fbo->handle();
+ // createFramebufferObject() bails out when the size is empty. In this case
+ // we cannot render either.
+ if (!outputTexture)
+ return;
renderControl->beginFrame();
+ QQuickRenderControlPrivate::FrameStatus frameStatus = QQuickRenderControlPrivate::get(renderControl)->frameStatus;
+ if (frameStatus == QQuickRenderControlPrivate::DeviceLostInBeginFrame) {
+ // graphics resources controlled by us must be released
+ invalidateRenderControl();
+ // skip this round and hope that the tlw's repaint manager will manage to reinitialize
+ deviceLost = true;
+ return;
+ }
+ if (frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
+ qWarning("QQuickWidget: Failed to begin recording a frame");
+ return;
+ }
if (needsSync) {
renderControl->polishItems();
@@ -342,24 +392,14 @@ void QQuickWidgetPrivate::render(bool needsSync)
renderControl->render();
renderControl->endFrame();
-
- context->makeCurrent(offscreenSurface);
- if (resolvedFbo) {
- QRect rect(QPoint(0, 0), fbo->size());
- QOpenGLFramebufferObject::blitFramebuffer(resolvedFbo, rect, fbo, rect);
- }
-
- static_cast<QOpenGLExtensions *>(context->functions())->flushShared();
-
- QOpenGLContextPrivate::get(context)->defaultFboRedirect = 0;
-#endif
} else {
//Software Renderer
if (needsSync) {
renderControl->polishItems();
renderControl->sync();
}
-
+ if (!offscreenWindow)
+ return;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(offscreenWindow);
auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
if (softwareRenderer && !softwareImage.isNull()) {
@@ -383,16 +423,6 @@ void QQuickWidgetPrivate::renderSceneGraph()
if (!q->isVisible() || fakeHidden)
return;
- if (!useSoftwareRenderer) {
-#if QT_CONFIG(opengl)
- if (!context) {
- qWarning("QQuickWidget: Attempted to render scene with no context");
- return;
- }
-#endif
- Q_ASSERT(offscreenSurface);
- }
-
render(true);
#if QT_CONFIG(graphicsview)
@@ -410,25 +440,35 @@ void QQuickWidgetPrivate::renderSceneGraph()
QImage QQuickWidgetPrivate::grabFramebuffer()
{
- if (!useSoftwareRenderer) {
-#if QT_CONFIG(opengl)
- if (!context)
- return QImage();
-
- context->makeCurrent(offscreenSurface);
-#endif
+ if (!useSoftwareRenderer && !rhi)
+ return QImage();
+
+ // grabWindow() does not work for the rhi case, we are in control of the
+ // render target, and so it is up to us to read it back. When the software
+ // renderer is in use, just call grabWindow().
+
+ if (outputTexture) {
+ render(true);
+ QRhiCommandBuffer *cb = nullptr;
+ rhi->beginOffscreenFrame(&cb);
+ QRhiResourceUpdateBatch *resUpd = rhi->nextResourceUpdateBatch();
+ QRhiReadbackResult readResult;
+ resUpd->readBackTexture(QRhiReadbackDescription(outputTexture), &readResult);
+ cb->resourceUpdate(resUpd);
+ rhi->endOffscreenFrame();
+ if (!readResult.data.isEmpty()) {
+ QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
+ readResult.pixelSize.width(), readResult.pixelSize.height(),
+ QImage::Format_RGBA8888_Premultiplied);
+ if (rhi->isYUpInFramebuffer())
+ return wrapperImage.mirrored();
+ else
+ return wrapperImage.copy();
+ }
+ return QImage();
}
-
- // grabWindow() does not work for the OpenGL + render control case, so we
- // prefer the FBO's toImage() if available. When the software renderer
- // is in use, however, there will be no FBO and we fall back to grabWindow()
- // instead.
- return
-#if QT_CONFIG(opengl)
- fbo != nullptr ? fbo->toImage() :
-#endif
- offscreenWindow->grabWindow();
+ return offscreenWindow->grabWindow();
}
// Intentionally not overriding the QQuickWindow's focusObject.
@@ -481,25 +521,48 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
size of the view. Alternatively the resizeMode may be set to SizeRootObjectToView which
will resize the view to the size of the root object.
- \note QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
+ \section1 Performance Considerations
+
+ QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
The restrictions on stacking order do not apply, making QQuickWidget the more flexible
alternative, behaving more like an ordinary widget.
- \note However, the above mentioned advantages come at the expense of performance.
- Unlike QQuickWindow and QQuickView, QQuickWidget requires rendering into OpenGL
- framebuffer objects, which needs to be enforced by calling
- QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi) at startup.
- This will naturally carry a minor performance hit.
+ However, the above mentioned advantages come at the expense of performance:
+ \list
+
+ \li Unlike QQuickWindow and QQuickView, QQuickWidget involves at least one
+ additional render pass targeting an offscreen color buffer, typically a 2D
+ texture, followed by drawing a texture quad. This means increased load
+ especially for the fragment processing of the GPU.
- \note Using QQuickWidget disables the threaded render loop on all platforms. This means that
- some of the benefits of threaded rendering, for example \l Animator classes and vsync driven
- animations, will not be available.
+ \li Using QQuickWidget disables the \l{threaded_render_loop}{threaded render loop} on all
+ platforms. This means that some of the benefits of threaded rendering, for example
+ \l Animator classes and vsync driven animations, will not be available.
+ \endlist
\note Avoid calling winId() on a QQuickWidget. This function triggers the creation of
a native window, resulting in reduced performance and possibly rendering glitches. The
entire purpose of QQuickWidget is to render Quick scenes without a separate native
window, hence making it a native widget should always be avoided.
+ \section1 Graphics API Support
+
+ QQuickWidget is functional with all the 3D graphics APIs supported by Qt
+ Quick, as well as the \c software backend. Other backends, for example
+ OpenVG, are not compatible however and attempting to construct a
+ QQuickWidget will lead to problems.
+
+ Overriding the platform's default graphics API is done the same way as with
+ QQuickWindow and QQuickView: either by calling
+ QQuickWindow::setGraphicsApi() early on before constructing the first
+ QQuickWidget, or by setting the \c{QSG_RHI_BACKEND} environment variable.
+
+ \note One top-level window can only use one single graphics API for
+ rendering. For example, attempting to place a QQuickWidget using Vulkan and
+ a QOpenGLWidget in the widget hierarchy of the same top-level window,
+ problems will occur and one of the widgets will not be rendering as
+ expected.
+
\section1 Scene Graph and Context Persistency
QQuickWidget honors QQuickWindow::isPersistentSceneGraph(), meaning that
@@ -509,20 +572,19 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
related resources be released whenever the widget becomes hidden. By default
persistency is enabled, just like with QQuickWindow.
- When running with the OpenGL backend of the scene graph, QQuickWindow
- offers the possibility to disable persistent OpenGL contexts as well. This
- setting is currently ignored by QQuickWidget and the context is always
- persistent. The OpenGL context is thus not destroyed when hiding the
- widget. The context is destroyed only when the widget is destroyed or when
- the widget gets reparented into another top-level widget's child hierarchy.
- However, some applications, in particular those that have their own
- graphics resources due to performing custom OpenGL rendering in the Qt
- Quick scene, may wish to disable the latter since they may not be prepared
- to handle the loss of the context when moving a QQuickWidget into another
- window. Such applications can set the
- QCoreApplication::AA_ShareOpenGLContexts attribute. For a discussion on the
- details of resource initialization and cleanup, refer to the QOpenGLWidget
- documentation.
+ When running with the OpenGL, QQuickWindow offers the possibility to
+ disable persistent OpenGL contexts as well. This setting is currently
+ ignored by QQuickWidget and the context is always persistent. The OpenGL
+ context is thus not destroyed when hiding the widget. The context is
+ destroyed only when the widget is destroyed or when the widget gets
+ reparented into another top-level widget's child hierarchy. However, some
+ applications, in particular those that have their own graphics resources
+ due to performing custom OpenGL rendering in the Qt Quick scene, may wish
+ to disable the latter since they may not be prepared to handle the loss of
+ the context when moving a QQuickWidget into another window. Such
+ applications can set the QCoreApplication::AA_ShareOpenGLContexts
+ attribute. For a discussion on the details of resource initialization and
+ cleanup, refer to the QOpenGLWidget documentation.
\note QQuickWidget offers less fine-grained control over its internal
OpenGL context than QOpenGLWidget, and there are subtle differences, most
@@ -550,13 +612,6 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
Qt::WA_TranslucentBackground on the top-level window, request an alpha channel, and
change the Qt Quick Scenegraph's clear color to Qt::transparent via setClearColor().
- \section1 Support when not using OpenGL
-
- In addition to OpenGL, the \c software backend of Qt Quick also supports
- QQuickWidget. Other backends, for example OpenVG, are not
- compatible however and attempting to construct a QQuickWidget will lead to
- problems.
-
\section1 Tab Key Handling
On press of the \c[TAB] key, the item inside the QQuickWidget gets focus. If
@@ -573,23 +628,22 @@ QImage QQuickWidgetPrivate::grabFramebuffer()
*/
/*!
- Constructs a QQuickWidget with the given \a parent.
- The default value of \a parent is 0.
+ Constructs a QQuickWidget with a default QML engine as a child of \a parent.
+ The default value of \a parent is \c nullptr.
*/
QQuickWidget::QQuickWidget(QWidget *parent)
: QWidget(*(new QQuickWidgetPrivate), parent, {})
{
- setMouseTracking(true);
- setFocusPolicy(Qt::StrongFocus);
d_func()->init();
}
/*!
- Constructs a QQuickWidget with the given QML \a source and \a parent.
- The default value of \a parent is 0.
+ Constructs a QQuickWidget with a default QML engine and the given QML \a source
+ as a child of \a parent.
-*/
+ The default value of \a parent is \c nullptr.
+ */
QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
: QQuickWidget(parent)
{
@@ -597,19 +651,15 @@ QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
}
/*!
- Constructs a QQuickWidget with the given QML \a engine and \a parent.
+ Constructs a QQuickWidget with the given QML \a engine as a child of \a parent.
- Note: In this case, the QQuickWidget does not own the given \a engine object;
+ \note The QQuickWidget does not take ownership of the given \a engine object;
it is the caller's responsibility to destroy the engine. If the \a engine is deleted
- before the view, status() will return QQuickWidget::Error.
-
- \sa Status, status(), errors()
+ before the view, \l status() will return \l QQuickWidget::Error.
*/
QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent)
: QWidget(*(new QQuickWidgetPrivate), parent, {})
{
- setMouseTracking(true);
- setFocusPolicy(Qt::StrongFocus);
d_func()->init(engine);
}
@@ -623,6 +673,16 @@ QQuickWidget::~QQuickWidget()
Q_D(QQuickWidget);
delete d->root;
d->root = nullptr;
+
+ if (d->rhi)
+ d->rhi->removeCleanupCallback(this);
+
+ // NB! resetting graphics resources must be done from this destructor,
+ // *not* from the private class' destructor. This is due to how destruction
+ // works and due to the QWidget dtor (for toplevels) destroying the repaint
+ // manager and rhi before the (QObject) private gets destroyed. Hence must
+ // do it here early on.
+ d->destroy();
}
/*!
@@ -927,7 +987,7 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &)
QString translatedMessage;
QString untranslatedMessage;
- QQuickWindowPrivate::rhiCreationFailureMessage(QLatin1String("OpenGL"), &translatedMessage, &untranslatedMessage);
+ QQuickWindowPrivate::rhiCreationFailureMessage(QLatin1String("QRhi"), &translatedMessage, &untranslatedMessage);
static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWidget::sceneGraphError);
const bool signalConnected = q->isSignalConnected(errorSignal);
@@ -942,74 +1002,100 @@ void QQuickWidgetPrivate::handleContextCreationFailure(const QSurfaceFormat &)
qFatal("%s", qPrintable(untranslatedMessage));
}
+static inline QPlatformBackingStoreRhiConfig::Api graphicsApiToBackingStoreRhiApi(QSGRendererInterface::GraphicsApi api)
+{
+ switch (api) {
+ case QSGRendererInterface::OpenGL:
+ return QPlatformBackingStoreRhiConfig::OpenGL;
+ case QSGRendererInterface::Vulkan:
+ return QPlatformBackingStoreRhiConfig::Vulkan;
+ case QSGRendererInterface::Direct3D11:
+ return QPlatformBackingStoreRhiConfig::D3D11;
+ case QSGRendererInterface::Direct3D12:
+ return QPlatformBackingStoreRhiConfig::D3D12;
+ case QSGRendererInterface::Metal:
+ return QPlatformBackingStoreRhiConfig::Metal;
+ default:
+ return QPlatformBackingStoreRhiConfig::Null;
+ }
+}
+
// Never called by Software Rendering backend
-void QQuickWidgetPrivate::createContext()
+void QQuickWidgetPrivate::initializeWithRhi()
{
-#if QT_CONFIG(opengl)
Q_Q(QQuickWidget);
+ // when reparenting, the rhi may suddenly be different
+ if (rhi) {
+ QRhi *backingStoreRhi = QWidgetPrivate::rhi();
+ if (backingStoreRhi && rhi != backingStoreRhi)
+ rhi = nullptr;
+ }
+
// On hide-show we may invalidate() (when !isPersistentSceneGraph) but our
// context is kept. We may need to initialize() again, though.
- const bool reinit = context && !offscreenWindow->isSceneGraphInitialized();
+ const bool onlyNeedsSgInit = rhi && !offscreenWindow->isSceneGraphInitialized();
- if (!reinit) {
- if (context)
+ if (!onlyNeedsSgInit) {
+ if (rhi)
return;
- context = new QOpenGLContext;
- context->setFormat(offscreenWindow->requestedFormat());
- const QWindow *win = q->window()->windowHandle();
- if (win && win->screen())
- context->setScreen(win->screen());
- QOpenGLContext *shareContext = qt_gl_global_share_context();
- if (!shareContext)
- shareContext = QWidgetPrivate::get(q->window())->shareContext();
- if (shareContext) {
- context->setShareContext(shareContext);
- context->setScreen(shareContext->screen());
+ if (QRhi *backingStoreRhi = QWidgetPrivate::rhi()) {
+ rhi = backingStoreRhi;
+ // We don't own the RHI, so make sure we clean up if it goes away
+ rhi->addCleanupCallback(q, [this](QRhi *rhi) {
+ if (this->rhi == rhi) {
+ invalidateRenderControl();
+ deviceLost = true;
+ this->rhi = nullptr;
+ }
+ });
}
- if (!context->create()) {
- delete context;
- context = nullptr;
- handleContextCreationFailure(offscreenWindow->requestedFormat());
- return;
+
+ if (!rhi) {
+ // The widget (and its parent chain, if any) may not be shown at
+ // all, yet one may still want to use it for grabs. This is
+ // ridiculous of course because the rendering infrastructure is
+ // tied to the top-level widget that initializes upon expose, but
+ // it has to be supported.
+ offscreenRenderer.setConfig(rhiConfig());
+ offscreenRenderer.setFormat(q->format());
+ // no window passed in, so no swapchain, but we get a functional QRhi which we own
+ if (offscreenRenderer.create())
+ rhi = offscreenRenderer.rhi();
}
- offscreenSurface = new QOffscreenSurface;
- // Pass the context's format(), which, now that the underlying platform context is created,
- // contains a QSurfaceFormat representing the _actual_ format of the underlying
- // configuration. This is essential to get a surface that is compatible with the context.
- offscreenSurface->setFormat(context->format());
- offscreenSurface->setScreen(context->screen());
- offscreenSurface->create();
+ // Could be that something else already initialized the window with some
+ // other graphics API for the QRhi, that's not good.
+ if (rhi && rhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()))) {
+ qWarning("The top-level window is not using the expected graphics API for composition, "
+ "'%s' is not compatible with this QQuickWidget",
+ rhi->backendName());
+ rhi = nullptr;
+ }
}
- if (context->makeCurrent(offscreenSurface)) {
+ if (rhi) {
if (!offscreenWindow->isSceneGraphInitialized()) {
- offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(context));
+ offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromRhi(rhi));
+#if QT_CONFIG(vulkan)
+ if (QWindow *w = q->window()->windowHandle())
+ offscreenWindow->setVulkanInstance(w->vulkanInstance());
+ else if (rhi == offscreenRenderer.rhi())
+ offscreenWindow->setVulkanInstance(QVulkanDefaultInstance::instance());
+#endif
renderControl->initialize();
}
- } else
-#endif
- qWarning("QQuickWidget: Failed to make context current");
-}
-
-// Never called by Software Rendering backend
-void QQuickWidgetPrivate::destroyContext()
-{
-#if QT_CONFIG(opengl)
- delete context;
- context = nullptr;
-#endif
- delete offscreenSurface;
- offscreenSurface = nullptr;
+ } else {
+ qWarning("QQuickWidget: Failed to get a QRhi from the top-level widget's window");
+ }
}
void QQuickWidget::createFramebufferObject()
{
Q_D(QQuickWidget);
- // Could come from Show -> createContext -> sceneGraphInitialized in which case the size may
+ // Could come from Show -> initializeWithRhi -> sceneGraphInitialized in which case the size may
// still be invalid on some platforms. Bail out. A resize will come later on.
if (size().isEmpty())
return;
@@ -1028,82 +1114,85 @@ void QQuickWidget::createFramebufferObject()
return;
}
-#if QT_CONFIG(opengl)
- if (!d->context) {
- qWarning("QQuickWidget: Attempted to create FBO with no context");
- return;
- }
-
- QOpenGLContext *shareWindowContext = QWidgetPrivate::get(window())->shareContext();
- if (shareWindowContext && d->context->shareContext() != shareWindowContext && !qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
- d->context->setShareContext(shareWindowContext);
- d->context->setScreen(shareWindowContext->screen());
- if (!d->context->create())
- qWarning("QQuickWidget: Failed to recreate context");
- // The screen may be different so we must recreate the offscreen surface too.
- // Unlike QOpenGLContext, QOffscreenSurface's create() does not recreate so have to destroy() first.
- d->offscreenSurface->destroy();
- d->offscreenSurface->setScreen(d->context->screen());
- d->offscreenSurface->create();
- }
-
- bool current = d->context->makeCurrent(d->offscreenSurface);
- if (!current) {
- qWarning("QQuickWidget: Failed to make context current when creating FBO");
+ if (!d->rhi) {
+ qWarning("QQuickWidget: Attempted to create output texture with no QRhi");
return;
}
int samples = d->requestedSamples;
- if (!QOpenGLExtensions(d->context).hasOpenGLExtension(QOpenGLExtensions::FramebufferMultisample))
+ if (d->rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer))
+ samples = QSGRhiSupport::chooseSampleCount(samples, d->rhi);
+ else
samples = 0;
- QOpenGLFramebufferObjectFormat format;
- format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- format.setSamples(samples);
-
- // The default framebuffer for normal windows have sRGB support on OS X which leads to the Qt Quick text item
- // utilizing sRGB blending. To get identical results with QQuickWidget we have to have our framebuffer backed
- // by an sRGB texture.
-#ifdef Q_OS_MACOS
- if (d->context->hasExtension("GL_ARB_framebuffer_sRGB")
- && d->context->hasExtension("GL_EXT_texture_sRGB")
- && d->context->hasExtension("GL_EXT_texture_sRGB_decode")) {
- format.setInternalTextureFormat(GL_SRGB8_ALPHA8_EXT);
- }
-#endif
-
const QSize fboSize = size() * devicePixelRatio();
- // Could be a simple hide - show, in which case the previous fbo is just fine.
- if (!d->fbo || d->fbo->size() != fboSize) {
- delete d->fbo;
- d->fbo = new QOpenGLFramebufferObject(fboSize, format);
+ // Could be a simple hide - show, in which case the previous texture is just fine.
+ if (!d->outputTexture) {
+ d->outputTexture = d->rhi->newTexture(QRhiTexture::RGBA8, fboSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
+ if (!d->outputTexture->create()) {
+ qWarning("QQuickWidget: failed to create output texture of size %dx%d",
+ fboSize.width(), fboSize.height());
+ }
}
-
- // When compositing in the backingstore, sampling the sRGB texture would perform an
- // sRGB-linear conversion which is not what we want when the final framebuffer (the window's)
- // is sRGB too. Disable the conversion.
-#ifdef Q_OS_MACOS
- if (format.internalTextureFormat() == GL_SRGB8_ALPHA8_EXT) {
- QOpenGLFunctions *funcs = d->context->functions();
- funcs->glBindTexture(GL_TEXTURE_2D, d->fbo->texture());
- funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
+ if (!d->depthStencil) {
+ d->depthStencil = d->rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, fboSize, samples);
+ if (!d->depthStencil->create()) {
+ qWarning("QQuickWidget: failed to create depth/stencil buffer of size %dx%d and sample count %d",
+ fboSize.width(), fboSize.height(), samples);
+ }
+ }
+ if (samples > 1 && !d->msaaBuffer) {
+ d->msaaBuffer = d->rhi->newRenderBuffer(QRhiRenderBuffer::Color, fboSize, samples);
+ if (!d->msaaBuffer->create()) {
+ qWarning("QQuickWidget: failed to create multisample renderbuffer of size %dx%d and sample count %d",
+ fboSize.width(), fboSize.height(), samples);
+ }
+ }
+ if (!d->rt) {
+ QRhiTextureRenderTargetDescription rtDesc;
+ QRhiColorAttachment colorAtt;
+ if (samples <= 1) {
+ colorAtt.setTexture(d->outputTexture);
+ } else {
+ colorAtt.setRenderBuffer(d->msaaBuffer);
+ colorAtt.setResolveTexture(d->outputTexture);
+ }
+ rtDesc.setColorAttachments({ colorAtt });
+ rtDesc.setDepthStencilBuffer(d->depthStencil);
+ d->rt = d->rhi->newTextureRenderTarget(rtDesc);
+ d->rtRp = d->rt->newCompatibleRenderPassDescriptor();
+ d->rt->setRenderPassDescriptor(d->rtRp);
+ d->rt->create();
+ }
+ if (d->outputTexture->pixelSize() != fboSize) {
+ d->outputTexture->setPixelSize(fboSize);
+ if (!d->outputTexture->create()) {
+ qWarning("QQuickWidget: failed to create resized output texture of size %dx%d",
+ fboSize.width(), fboSize.height());
+ }
+ d->depthStencil->setPixelSize(fboSize);
+ if (!d->depthStencil->create()) {
+ qWarning("QQuickWidget: failed to create resized depth/stencil buffer of size %dx%d",
+ fboSize.width(), fboSize.height());
+ }
+ if (d->msaaBuffer) {
+ d->msaaBuffer->setPixelSize(fboSize);
+ if (!d->msaaBuffer->create()) {
+ qWarning("QQuickWidget: failed to create resized multisample renderbuffer of size %dx%d",
+ fboSize.width(), fboSize.height());
+ }
+ }
}
-#endif
- GLuint textureId = d->fbo->texture();
- d->offscreenWindow->setRenderTarget( QQuickRenderTarget::fromOpenGLTexture(textureId, fboSize, samples));
+ d->offscreenWindow->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(d->rt));
d->renderControl->setSamples(samples);
- if (samples > 0)
- d->resolvedFbo = new QOpenGLFramebufferObject(fboSize);
-
// Sanity check: The window must not have an underlying platform window.
// Having one would mean create() was called and platforms that only support
// a single native window were in trouble.
Q_ASSERT(!d->offscreenWindow->handle());
-#endif
}
void QQuickWidget::destroyFramebufferObject()
@@ -1115,12 +1204,16 @@ void QQuickWidget::destroyFramebufferObject()
return;
}
-#if QT_CONFIG(opengl)
- delete d->fbo;
- d->fbo = nullptr;
- delete d->resolvedFbo;
- d->resolvedFbo = nullptr;
-#endif
+ delete d->rt;
+ d->rt = nullptr;
+ delete d->rtRp;
+ d->rtRp = nullptr;
+ delete d->depthStencil;
+ d->depthStencil = nullptr;
+ delete d->msaaBuffer;
+ d->msaaBuffer = nullptr;
+ delete d->outputTexture;
+ d->outputTexture = nullptr;
}
QQuickWidget::ResizeMode QQuickWidget::resizeMode() const
@@ -1198,17 +1291,35 @@ void QQuickWidgetPrivate::setRootObject(QObject *obj)
}
}
-#if QT_CONFIG(opengl)
-GLuint QQuickWidgetPrivate::textureId() const
+QPlatformBackingStoreRhiConfig QQuickWidgetPrivate::rhiConfig() const
+{
+ const_cast<QQuickWidgetPrivate *>(this)->ensureBackingScene();
+ if (useSoftwareRenderer)
+ return {};
+
+ QPlatformBackingStoreRhiConfig config(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()));
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(offscreenWindow);
+ // This is only here to support some of the env.vars. (such as
+ // QSG_RHI_DEBUG_LAYER). There is currently no way to set a
+ // QQuickGraphicsConfiguration for a QQuickWidget, which means things like
+ // the pipeline cache are just not available. That is something to support
+ // on the widget/backingstore level since that's where the QRhi is
+ // controlled in this case.
+ const bool debugLayerRequested = wd->graphicsConfig.isDebugLayerEnabled();
+ config.setDebugLayer(debugLayerRequested);
+ return config;
+}
+
+QWidgetPrivate::TextureData QQuickWidgetPrivate::texture() const
{
Q_Q(const QQuickWidget);
if (!q->isWindow() && q->internalWinId()) {
qWarning() << "QQuickWidget cannot be used as a native child widget."
<< "Consider setting Qt::AA_DontCreateNativeWidgetSiblings";
- return 0;
+ return {};
}
- return resolvedFbo ? resolvedFbo->texture()
- : (fbo ? fbo->texture() : 0);
+ return { outputTexture, nullptr };
}
QPlatformTextureList::Flags QQuickWidgetPrivate::textureListFlags()
@@ -1217,7 +1328,6 @@ QPlatformTextureList::Flags QQuickWidgetPrivate::textureListFlags()
flags |= QPlatformTextureList::NeedsPremultipliedAlphaBlending;
return flags;
}
-#endif
/*!
\internal
@@ -1307,13 +1417,12 @@ void QQuickWidget::resizeEvent(QResizeEvent *e)
createFramebufferObject();
}
} else {
-#if QT_CONFIG(opengl)
- if (d->context) {
+ if (d->rhi) {
// Bail out when receiving a resize after scenegraph invalidation. This can happen
// during hide - resize - show sequences and also during application exit.
- if (!d->fbo && !d->offscreenWindow->isSceneGraphInitialized())
+ if (!d->outputTexture && !d->offscreenWindow->isSceneGraphInitialized())
return;
- if (!d->fbo || d->fbo->size() != size() * devicePixelRatio()) {
+ if (!d->outputTexture || d->outputTexture->pixelSize() != size() * devicePixelRatio()) {
needsSync = true;
createFramebufferObject();
}
@@ -1321,14 +1430,13 @@ void QQuickWidget::resizeEvent(QResizeEvent *e)
// This will result in a scenegraphInitialized() signal which
// is connected to createFramebufferObject().
needsSync = true;
- d->createContext();
+ d->initializeWithRhi();
}
- if (!d->context) {
- qWarning("QQuickWidget::resizeEvent() no OpenGL context");
+ if (!d->rhi) {
+ qWarning("QQuickWidget::resizeEvent() no QRhi");
return;
}
-#endif
}
d->render(needsSync);
@@ -1383,6 +1491,8 @@ void QQuickWidget::mouseMoveEvent(QMouseEvent *e)
// top-level window always.
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ // It's not just the timestamp but also the globalPressPosition, velocity etc.
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1398,10 +1508,12 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
// See QTBUG-25831
QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ pressEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent);
e->setAccepted(pressEvent.isAccepted());
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
}
@@ -1412,7 +1524,7 @@ void QQuickWidget::showEvent(QShowEvent *)
bool shouldTriggerUpdate = true;
if (!d->useSoftwareRenderer) {
- d->createContext();
+ d->initializeWithRhi();
if (d->offscreenWindow->isSceneGraphInitialized()) {
shouldTriggerUpdate = false;
@@ -1434,7 +1546,7 @@ void QQuickWidget::showEvent(QShowEvent *)
if (shouldTriggerUpdate)
triggerUpdate();
- // note offscreenWindow is "QQuickOffScreenWindow" instance
+ // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance
d->offscreenWindow->setVisible(true);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->setParentWindow(d->offscreenWindow, window()->windowHandle());
@@ -1446,7 +1558,7 @@ void QQuickWidget::hideEvent(QHideEvent *)
Q_D(QQuickWidget);
if (!d->offscreenWindow->isPersistentSceneGraph())
d->invalidateRenderControl();
- // note offscreenWindow is "QQuickOffScreenWindow" instance
+ // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance
d->offscreenWindow->setVisible(false);
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->setParentWindow(d->offscreenWindow, d->offscreenWindow);
@@ -1461,6 +1573,7 @@ void QQuickWidget::mousePressEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1474,6 +1587,7 @@ void QQuickWidget::mouseReleaseEvent(QMouseEvent *e)
QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
e->button(), e->buttons(), e->modifiers(), e->source());
+ mappedEvent.setTimestamp(e->timestamp());
QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
e->setAccepted(mappedEvent.isAccepted());
}
@@ -1556,9 +1670,23 @@ bool QQuickWidget::event(QEvent *e)
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
- case QEvent::TouchCancel:
+ case QEvent::TouchCancel: {
// Touch events only have local and global positions, no need to map.
- return QCoreApplication::sendEvent(d->offscreenWindow, e);
+ bool res = QCoreApplication::sendEvent(d->offscreenWindow, e);
+ if (e->isAccepted() && e->type() == QEvent::TouchBegin) {
+ // If the TouchBegin got accepted, then make sure all points that have
+ // an exclusive grabber are also accepted so that the widget code for
+ // delivering touch events make this widget an implicit grabber of those
+ // points.
+ QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e);
+ auto deliveredPoints = pointerEvent->points();
+ for (auto &point : deliveredPoints) {
+ if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty())
+ point.setAccepted(true);
+ }
+ }
+ return res;
+ }
case QEvent::FocusAboutToChange:
return QCoreApplication::sendEvent(d->offscreenWindow, e);
@@ -1576,36 +1704,37 @@ bool QQuickWidget::event(QEvent *e)
return eventResult;
}
+ case QEvent::WindowAboutToChangeInternal:
+ if (d->rhi)
+ d->rhi->removeCleanupCallback(this);
+ d->invalidateRenderControl();
+ d->deviceLost = true;
+ d->rhi = nullptr;
+ break;
+
case QEvent::WindowChangeInternal:
d->handleWindowChange();
break;
case QEvent::ScreenChangeInternal:
- if (QWindow *window = this->window()->windowHandle()) {
- QScreen *newScreen = window->screen();
-
- if (d->offscreenWindow)
- d->offscreenWindow->setScreen(newScreen);
- if (d->offscreenSurface)
- d->offscreenSurface->setScreen(newScreen);
-#if QT_CONFIG(opengl)
- if (d->context)
- d->context->setScreen(newScreen);
-#endif
- }
-
- if (d->useSoftwareRenderer
-#if QT_CONFIG(opengl)
- || d->fbo
-#endif
- ) {
+ {
+ QScreen *newScreen = screen();
+ if (d->offscreenWindow)
+ d->offscreenWindow->setScreen(newScreen);
+ break;
+ }
+ case QEvent::DevicePixelRatioChange:
+ if (d->useSoftwareRenderer || d->outputTexture) {
// This will check the size taking the devicePixelRatio into account
// and recreate if needed.
createFramebufferObject();
d->render(true);
}
+ if (d->offscreenWindow) {
+ QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
+ QGuiApplication::sendEvent(d->offscreenWindow, &dprChangeEvent);
+ }
break;
-
case QEvent::Show:
case QEvent::Move:
d->updatePosition();
@@ -1820,10 +1949,8 @@ void QQuickWidget::propagateFocusObjectChanged(QObject *focusObject)
emit window->focusObjectChanged(focusObject);
}
-#if QT_CONFIG(opengl)
-Q_CONSTRUCTOR_FUNCTION(qt_registerDefaultPlatformBackingStoreOpenGLSupport);
-#endif
-
QT_END_NAMESPACE
+#include "moc_qquickwidget_p.cpp"
+
#include "moc_qquickwidget.cpp"