aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickdeliveryagent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/util/qquickdeliveryagent.cpp')
-rw-r--r--src/quick/util/qquickdeliveryagent.cpp5841
1 files changed, 5841 insertions, 0 deletions
diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp
new file mode 100644
index 0000000000..acc220bba3
--- /dev/null
+++ b/src/quick/util/qquickdeliveryagent.cpp
@@ -0,0 +1,5841 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+#include "qquickwindow.h"
+#include "qquickwindow_p.h"
+
+#include "qquickitem.h"
+#include "qquickitem_p.h"
+#include "qquickevents_p_p.h"
+#include "qquickgraphicsdevice_p.h"
+
+#if QT_CONFIG(quick_draganddrop)
+#include <private/qquickdrag_p.h>
+#endif
+#include <private/qquickhoverhandler_p.h>
+#include <private/qquickpointerhandler_p.h>
+
+#include <QtQuick/private/qsgrenderer_p.h>
+#include <QtQuick/private/qsgplaintexture_p.h>
+#include <private/qsgrenderloop_p.h>
+#include <private/qsgrhisupport_p.h>
+#include <private/qquickrendercontrol_p.h>
+#include <private/qquickanimatorcontroller_p.h>
+#include <private/qquickpointerhandler_p_p.h>
+#include <private/qquickprofiler_p.h>
+
+#include <private/qguiapplication_p.h>
+#include <QtGui/QInputMethod>
+
+#include <private/qabstractanimation_p.h>
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qmatrix4x4.h>
+#include <QtGui/private/qevent_p.h>
+#include <QtGui/private/qpointingdevice_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qabstractanimation.h>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QRunnable>
+#include <QtQml/qqmlincubator.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/private/qqmlmetatype_p.h>
+
+#include <QtQuick/private/qquickpixmapcache_p.h>
+
+#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qqmldebugconnector_p.h>
+#include <private/qsgdefaultrendercontext_p.h>
+#if QT_CONFIG(opengl)
+#include <private/qopengl_p.h>
+#include <QOpenGLContext>
+#endif
+#ifndef QT_NO_DEBUG_STREAM
+#include <private/qdebug_p.h>
+#endif
+
+#include <QtGui/private/qrhi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTouch, "qt.quick.touch")
+Q_LOGGING_CATEGORY(lcTouchCmprs, "qt.quick.touch.compression")
+Q_LOGGING_CATEGORY(lcTouchTarget, "qt.quick.touch.target")
+Q_LOGGING_CATEGORY(lcMouse, "qt.quick.mouse")
+Q_LOGGING_CATEGORY(lcMouseTarget, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(lcPtr, "qt.quick.pointer")
+Q_LOGGING_CATEGORY(lcPtrGrab, "qt.quick.pointer.grab")
+Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
+Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
+Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
+Q_LOGGING_CATEGORY(lcHoverTrace, "qt.quick.hover.trace")
+Q_LOGGING_CATEGORY(lcFocus, "qt.quick.focus")
+Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty")
+Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
+
+extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1);
+
+bool QQuickWindowPrivate::defaultAlphaBuffer = false;
+
+#if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE)
+QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE;
+#else
+QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering;
+#endif
+
+void QQuickWindowPrivate::updateFocusItemTransform()
+{
+#if QT_CONFIG(im)
+ Q_Q(QQuickWindow);
+ QQuickItem *focus = q->activeFocusItem();
+ if (focus && QGuiApplication::focusObject() == focus) {
+ QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(focus);
+ QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
+ QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
+ focus->updateInputMethod(Qt::ImInputItemClipRectangle);
+ }
+#endif
+}
+
+class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
+{
+ Q_OBJECT
+
+public:
+ QQuickWindowIncubationController(QSGRenderLoop *loop)
+ : m_renderLoop(loop), m_timer(0)
+ {
+ // Allow incubation for 1/3 of a frame.
+ m_incubation_time = qMax(1, int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3);
+
+ QAnimationDriver *animationDriver = m_renderLoop->animationDriver();
+ if (animationDriver) {
+ connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()));
+ connect(m_renderLoop, SIGNAL(timeToIncubate()), this, SLOT(incubate()));
+ }
+ }
+
+protected:
+ void timerEvent(QTimerEvent *) override
+ {
+ killTimer(m_timer);
+ m_timer = 0;
+ incubate();
+ }
+
+ void incubateAgain() {
+ if (m_timer == 0) {
+ // Wait for a while before processing the next batch. Using a
+ // timer to avoid starvation of system events.
+ m_timer = startTimer(m_incubation_time);
+ }
+ }
+
+public slots:
+ void incubate() {
+ if (m_renderLoop && incubatingObjectCount()) {
+ if (m_renderLoop->interleaveIncubation()) {
+ incubateFor(m_incubation_time);
+ } else {
+ incubateFor(m_incubation_time * 2);
+ if (incubatingObjectCount())
+ incubateAgain();
+ }
+ }
+ }
+
+ void animationStopped() { incubate(); }
+
+protected:
+ void incubatingObjectCountChanged(int count) override
+ {
+ if (count && m_renderLoop && !m_renderLoop->interleaveIncubation())
+ incubateAgain();
+ }
+
+private:
+ QPointer<QSGRenderLoop> m_renderLoop;
+ int m_incubation_time;
+ int m_timer;
+};
+
+#include "qquickwindow.moc"
+#include "moc_qquickwindow_p.cpp"
+
+
+#if QT_CONFIG(accessibility)
+/*!
+ Returns an accessibility interface for this window, or 0 if such an
+ interface cannot be created.
+*/
+QAccessibleInterface *QQuickWindow::accessibleRoot() const
+{
+ return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
+}
+#endif
+
+
+/*
+Focus behavior
+==============
+
+Prior to being added to a valid window items can set and clear focus with no
+effect. Only once items are added to a window (by way of having a parent set that
+already belongs to a window) do the focus rules apply. Focus goes back to
+having no effect if an item is removed from a window.
+
+When an item is moved into a new focus scope (either being added to a window
+for the first time, or having its parent changed), if the focus scope already has
+a scope focused item that takes precedence over the item being added. Otherwise,
+the focus of the added tree is used. In the case of a tree of items being
+added to a window for the first time, which may have a conflicted focus state (two
+or more items in one scope having focus set), the same rule is applied item by item -
+thus the first item that has focus will get it (assuming the scope doesn't already
+have a scope focused item), and the other items will have their focus cleared.
+*/
+
+QQuickRootItem::QQuickRootItem()
+{
+}
+
+/*! \reimp */
+void QQuickWindow::exposeEvent(QExposeEvent *)
+{
+ Q_D(QQuickWindow);
+ if (d->windowManager)
+ d->windowManager->exposureChanged(this);
+}
+
+/*! \reimp */
+void QQuickWindow::resizeEvent(QResizeEvent *ev)
+{
+ Q_D(QQuickWindow);
+ if (d->contentItem)
+ d->contentItem->setSize(ev->size());
+ if (d->windowManager)
+ d->windowManager->resize(this);
+}
+
+/*! \reimp */
+void QQuickWindow::showEvent(QShowEvent *)
+{
+ Q_D(QQuickWindow);
+ if (d->windowManager)
+ d->windowManager->show(this);
+}
+
+/*! \reimp */
+void QQuickWindow::hideEvent(QHideEvent *)
+{
+ Q_D(QQuickWindow);
+ if (d->windowManager)
+ d->windowManager->hide(this);
+}
+
+/*! \reimp */
+void QQuickWindow::closeEvent(QCloseEvent *e)
+{
+ QQuickCloseEvent qev;
+ qev.setAccepted(e->isAccepted());
+ emit closing(&qev);
+ e->setAccepted(qev.isAccepted());
+}
+
+/*! \reimp */
+void QQuickWindow::focusOutEvent(QFocusEvent *ev)
+{
+ Q_D(QQuickWindow);
+ if (d->contentItem)
+ d->contentItem->setFocus(false, ev->reason());
+}
+
+/*! \reimp */
+void QQuickWindow::focusInEvent(QFocusEvent *ev)
+{
+ Q_D(QQuickWindow);
+ if (d->contentItem)
+ d->contentItem->setFocus(true, ev->reason());
+ d->updateFocusItemTransform();
+}
+
+#if QT_CONFIG(im)
+static bool transformDirtyOnItemOrAncestor(const QQuickItem *item)
+{
+ while (item) {
+ if (QQuickItemPrivate::get(item)->dirtyAttributes & (
+ QQuickItemPrivate::TransformOrigin |
+ QQuickItemPrivate::Transform |
+ QQuickItemPrivate::BasicTransform |
+ QQuickItemPrivate::Position |
+ QQuickItemPrivate::Size |
+ QQuickItemPrivate::ParentChanged |
+ QQuickItemPrivate::Clip)) {
+ return true;
+ }
+ item = item->parentItem();
+ }
+ return false;
+}
+#endif
+
+/*!
+ * \internal
+
+ A "polish loop" can occur inside QQuickWindowPrivate::polishItems(). It is when an item calls
+ polish() on an(other?) item from updatePolish(). If this anomaly happens repeatedly and without
+ interruption (of a well-behaved updatePolish() that doesn't call polish()), it is a strong
+ indication that we are heading towards an infinite polish loop. A polish loop is not a bug in
+ Qt Quick - it is a bug caused by ill-behaved items put in the scene.
+
+ We can detect this sequence of polish loops easily, since the
+ QQuickWindowPrivate::itemsToPolish is basically a stack: polish() will push to it, and
+ polishItems() will pop from it.
+ Therefore if updatePolish() calls polish(), the immediate next item polishItems() processes is
+ the item that was polished by the previous call to updatePolish().
+ We therefore just need to count the number of polish loops we detected in _sequence_.
+*/
+struct PolishLoopDetector
+{
+ PolishLoopDetector(const QVector<QQuickItem*> &itemsToPolish)
+ : itemsToPolish(itemsToPolish)
+ {
+ }
+
+ /*
+ * returns true when it detected a likely infinite loop
+ * (suggests it should abort the polish loop)
+ **/
+ bool check(QQuickItem *item, int itemsRemainingBeforeUpdatePolish)
+ {
+ if (itemsToPolish.count() > itemsRemainingBeforeUpdatePolish) {
+ // Detected potential polish loop.
+ ++numPolishLoopsInSequence;
+ if (numPolishLoopsInSequence >= 1000) {
+ // Start to warn about polish loop after 1000 consecutive polish loops
+ if (numPolishLoopsInSequence == 100000) {
+ // We have looped 100,000 times without actually reducing the list of items to
+ // polish, give up for now.
+ // This is not a fix, just a remedy so that the application can be somewhat
+ // responsive.
+ numPolishLoopsInSequence = 0;
+ return true;
+ } else if (numPolishLoopsInSequence < 1005) {
+ // Show the 5 next items involved in the polish loop.
+ // (most likely they will be the same 5 items...)
+ QQuickItem *guiltyItem = itemsToPolish.last();
+ qmlWarning(item) << "possible QQuickItem::polish() loop";
+
+ auto typeAndObjectName = [](QQuickItem *item) {
+ QString typeName = QQmlMetaType::prettyTypeName(item);
+ QString objName = item->objectName();
+ if (!objName.isNull())
+ return QLatin1String("%1(%2)").arg(typeName, objName);
+ return typeName;
+ };
+
+ qmlWarning(guiltyItem) << typeAndObjectName(guiltyItem)
+ << " called polish() inside updatePolish() of " << typeAndObjectName(item);
+
+ if (numPolishLoopsInSequence == 1004)
+ // Enough warnings. Reset counter in order to speed things up and re-detect
+ // more loops
+ numPolishLoopsInSequence = 0;
+ }
+ }
+ } else {
+ numPolishLoopsInSequence = 0;
+ }
+ return false;
+ }
+ const QVector<QQuickItem*> &itemsToPolish; // Just a ref to the one in polishItems()
+ int numPolishLoopsInSequence = 0;
+};
+
+void QQuickWindowPrivate::polishItems()
+{
+ // An item can trigger polish on another item, or itself for that matter,
+ // during its updatePolish() call. Because of this, we cannot simply
+ // iterate through the set, we must continue pulling items out until it
+ // is empty.
+ // In the case where polish is called from updatePolish() either directly
+ // or indirectly, we use a PolishLoopDetector to determine if a warning should
+ // be printed to the user.
+
+ PolishLoopDetector polishLoopDetector(itemsToPolish);
+ while (!itemsToPolish.isEmpty()) {
+ QQuickItem *item = itemsToPolish.takeLast();
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->polishScheduled = false;
+ const int itemsRemaining = itemsToPolish.count();
+ itemPrivate->updatePolish();
+ item->updatePolish();
+ if (polishLoopDetector.check(item, itemsRemaining) == true)
+ break;
+ }
+
+#if QT_CONFIG(im)
+ if (QQuickItem *focusItem = q_func()->activeFocusItem()) {
+ // If the current focus item, or any of its anchestors, has changed location
+ // inside the window, we need inform IM about it. This to ensure that overlays
+ // such as selection handles will be updated.
+ const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject());
+ const bool hasImEnabled = focusItem->inputMethodQuery(Qt::ImEnabled).toBool();
+ if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(focusItem))
+ updateFocusItemTransform();
+ }
+#endif
+}
+
+/*!
+ * Schedules the window to render another frame.
+ *
+ * Calling QQuickWindow::update() differs from QQuickItem::update() in that
+ * it always triggers a repaint, regardless of changes in the underlying
+ * scene graph or not.
+ */
+void QQuickWindow::update()
+{
+ Q_D(QQuickWindow);
+ if (d->windowManager)
+ d->windowManager->update(this);
+ else if (d->renderControl)
+ QQuickRenderControlPrivate::get(d->renderControl)->update();
+}
+
+static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio)
+{
+ if (item->flags() & QQuickItem::ItemHasContents) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio);
+ }
+
+ QList <QQuickItem *> items = item->childItems();
+ for (int i = 0; i < items.size(); ++i)
+ updatePixelRatioHelper(items.at(i), pixelRatio);
+}
+
+void QQuickWindow::physicalDpiChanged()
+{
+ Q_D(QQuickWindow);
+ const qreal newPixelRatio = screen()->devicePixelRatio();
+ if (qFuzzyCompare(newPixelRatio, d->devicePixelRatio))
+ return;
+ d->devicePixelRatio = newPixelRatio;
+ if (d->contentItem)
+ updatePixelRatioHelper(d->contentItem, newPixelRatio);
+}
+
+void QQuickWindow::handleScreenChanged(QScreen *screen)
+{
+ Q_D(QQuickWindow);
+ if (screen) {
+ physicalDpiChanged();
+ // When physical DPI changes on the same screen, either the resolution or the device pixel
+ // ratio changed. We must check what it is. Device pixel ratio does not have its own
+ // ...Changed() signal.
+ d->physicalDpiChangedConnection = connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)),
+ this, SLOT(physicalDpiChanged()));
+ } else {
+ disconnect(d->physicalDpiChangedConnection);
+ }
+
+ d->forcePolish();
+}
+
+void forcePolishHelper(QQuickItem *item)
+{
+ if (item->flags() & QQuickItem::ItemHasContents) {
+ item->polish();
+ }
+
+ QList <QQuickItem *> items = item->childItems();
+ for (int i=0; i<items.size(); ++i)
+ forcePolishHelper(items.at(i));
+}
+
+/*!
+ Schedules polish events on all items in the scene.
+*/
+void QQuickWindowPrivate::forcePolish()
+{
+ Q_Q(QQuickWindow);
+ if (!q->screen())
+ return;
+ forcePolishHelper(contentItem);
+}
+
+void forceUpdate(QQuickItem *item)
+{
+ if (item->flags() & QQuickItem::ItemHasContents)
+ item->update();
+ QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
+
+ QList <QQuickItem *> items = item->childItems();
+ for (int i=0; i<items.size(); ++i)
+ forceUpdate(items.at(i));
+}
+
+void QQuickWindowRenderTarget::reset(QRhi *rhi, QSGRenderer *renderer)
+{
+ if (rhi) {
+ if (renderer)
+ renderer->invalidatePipelineCacheDependency(rpDesc);
+ if (owns) {
+ delete renderTarget;
+ delete rpDesc;
+ delete texture;
+ delete depthStencil;
+ }
+ }
+
+ renderTarget = nullptr;
+ rpDesc = nullptr;
+ texture = nullptr;
+ depthStencil = nullptr;
+ owns = false;
+}
+
+void QQuickWindowPrivate::ensureCustomRenderTarget()
+{
+ // resolve() can be expensive when importing an existing native texture, so
+ // it is important to only do it when the QQuickRenderTarget* was really changed
+ if (!redirect.renderTargetDirty || !rhi)
+ return;
+
+ redirect.renderTargetDirty = false;
+
+ redirect.rt.reset(rhi, renderer);
+
+ // a default constructed QQuickRenderTarget means no redirection
+ if (customRenderTarget.isNull())
+ return;
+
+ QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt);
+}
+
+void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb)
+{
+ // ownership not transferred
+ redirect.commandBuffer = cb;
+}
+
+void QQuickWindowPrivate::syncSceneGraph()
+{
+ Q_Q(QQuickWindow);
+
+ ensureCustomRenderTarget();
+
+ // Calculate the dpr the same way renderSceneGraph() will.
+ qreal devicePixelRatio = q->effectiveDevicePixelRatio();
+ if (redirect.rt.renderTarget && !QQuickRenderControl::renderWindowFor(q))
+ devicePixelRatio = 1;
+
+ QRhiCommandBuffer *cb = nullptr;
+ if (rhi) {
+ if (redirect.commandBuffer)
+ cb = redirect.commandBuffer;
+ else
+ cb = swapchain->currentFrameCommandBuffer();
+ }
+ context->prepareSync(devicePixelRatio, cb, graphicsConfig);
+
+ animationController->beforeNodeSync();
+
+ emit q->beforeSynchronizing();
+ runAndClearJobs(&beforeSynchronizingJobs);
+ if (!renderer) {
+ forceUpdate(contentItem);
+
+ QSGRootNode *rootNode = new QSGRootNode;
+ rootNode->appendChildNode(QQuickItemPrivate::get(contentItem)->itemNode());
+ const bool useDepth = graphicsConfig.isDepthBufferEnabledFor2D();
+ const QSGRendererInterface::RenderMode renderMode = useDepth ? QSGRendererInterface::RenderMode2D
+ : QSGRendererInterface::RenderMode2DNoDepthBuffer;
+ renderer = context->createRenderer(renderMode);
+ renderer->setRootNode(rootNode);
+ }
+
+ updateDirtyNodes();
+
+ animationController->afterNodeSync();
+
+ // Copy the current state of clearing from window into renderer.
+ renderer->setClearColor(clearColor);
+ // Cannot skip clearing the color buffer in Qt 6 anymore.
+ const QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearColorBuffer
+ | QSGAbstractRenderer::ClearStencilBuffer
+ | QSGAbstractRenderer::ClearDepthBuffer;
+ renderer->setClearMode(mode);
+
+ renderer->setVisualizationMode(visualizationMode);
+
+ emit q->afterSynchronizing();
+ runAndClearJobs(&afterSynchronizingJobs);
+}
+
+void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud)
+{
+ QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
+ emit w->beforeRenderPassRecording();
+}
+
+void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud)
+{
+ QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
+ emit w->afterRenderPassRecording();
+}
+
+void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfaceSize)
+{
+ Q_Q(QQuickWindow);
+ if (!renderer)
+ return;
+
+ if (rhi) {
+ ensureCustomRenderTarget();
+ QRhiRenderTarget *rt;
+ QRhiRenderPassDescriptor *rp;
+ QRhiCommandBuffer *cb;
+ if (redirect.rt.renderTarget) {
+ rt = redirect.rt.renderTarget;
+ rp = rt->renderPassDescriptor();
+ if (!rp) {
+ qWarning("Custom render target is set but no renderpass descriptor has been provided.");
+ return;
+ }
+ cb = redirect.commandBuffer;
+ if (!cb) {
+ qWarning("Custom render target is set but no command buffer has been provided.");
+ return;
+ }
+ } else {
+ if (!swapchain) {
+ qWarning("QQuickWindow: No render target (neither swapchain nor custom target was provided)");
+ return;
+ }
+ rt = swapchain->currentFrameRenderTarget();
+ rp = rpDescForSwapchain;
+ cb = swapchain->currentFrameCommandBuffer();
+ }
+ context->beginNextRhiFrame(renderer, rt, rp, cb,
+ emitBeforeRenderPassRecording,
+ emitAfterRenderPassRecording,
+ q);
+ } else {
+ context->beginNextFrame(renderer,
+ emitBeforeRenderPassRecording,
+ emitAfterRenderPassRecording,
+ q);
+ }
+
+ animationController->advance();
+ emit q->beforeRendering();
+ runAndClearJobs(&beforeRenderingJobs);
+
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
+ const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
+ if (flipY)
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
+ const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
+ if (redirect.rt.renderTarget) {
+ QRect rect(QPoint(0, 0), redirect.rt.renderTarget->pixelSize());
+ renderer->setDeviceRect(rect);
+ renderer->setViewportRect(rect);
+ if (QQuickRenderControl::renderWindowFor(q)) {
+ renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size), matrixFlags);
+ renderer->setDevicePixelRatio(devicePixelRatio);
+ } else {
+ renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()), matrixFlags);
+ renderer->setDevicePixelRatio(1);
+ }
+ } else {
+ QSize pixelSize;
+ QSizeF logicalSize;
+ if (surfaceSize.isEmpty()) {
+ pixelSize = size * devicePixelRatio;
+ logicalSize = size;
+ } else {
+ pixelSize = surfaceSize;
+ logicalSize = QSizeF(surfaceSize) / devicePixelRatio;
+ }
+ QRect rect(QPoint(0, 0), pixelSize);
+ renderer->setDeviceRect(rect);
+ renderer->setViewportRect(rect);
+ renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags);
+ renderer->setDevicePixelRatio(devicePixelRatio);
+ }
+
+ if (rhi) {
+ context->renderNextRhiFrame(renderer);
+ } else {
+ // This is the software backend (or some custom scenegraph context
+ // plugin) in practice, because the default implementation always
+ // hits the QRhi-based path in Qt 6.
+ context->renderNextFrame(renderer);
+ }
+
+ emit q->afterRendering();
+ runAndClearJobs(&afterRenderingJobs);
+
+ if (rhi)
+ context->endNextRhiFrame(renderer);
+ else
+ context->endNextFrame(renderer);
+
+ if (renderer && renderer->hasVisualizationModeWithContinuousUpdate()) {
+ // For the overdraw visualizer. This update is not urgent so avoid a
+ // direct update() call, this is only here to keep the overdraw
+ // visualization box rotating even when the scene is static.
+ QCoreApplication::postEvent(q, new QEvent(QEvent::Type(FullUpdateRequest)));
+ }
+}
+
+QQuickWindowPrivate::QQuickWindowPrivate()
+ : contentItem(nullptr)
+ , activeFocusItem(nullptr)
+#if QT_CONFIG(cursor)
+ , cursorItem(nullptr)
+ , cursorHandler(nullptr)
+#endif
+#if QT_CONFIG(quick_draganddrop)
+ , dragGrabber(nullptr)
+#endif
+ , touchMouseId(-1)
+ , touchMouseDevice(nullptr)
+ , touchMousePressTimestamp(0)
+ , dirtyItemList(nullptr)
+ , devicePixelRatio(0)
+ , context(nullptr)
+ , renderer(nullptr)
+ , windowManager(nullptr)
+ , renderControl(nullptr)
+ , pointerEventRecursionGuard(0)
+ , clearColor(Qt::white)
+ , persistentGraphics(true)
+ , persistentSceneGraph(true)
+ , lastWheelEventAccepted(false)
+ , componentCompleted(true)
+ , allowChildEventFiltering(true)
+ , allowDoubleClick(true)
+ , lastFocusReason(Qt::OtherFocusReason)
+ , incubationController(nullptr)
+ , hasActiveSwapchain(false)
+ , hasRenderableSwapchain(false)
+ , swapchainJustBecameRenderable(false)
+{
+#if QT_CONFIG(quick_draganddrop)
+ dragGrabber = new QQuickDragGrabber;
+#endif
+}
+
+QQuickWindowPrivate::~QQuickWindowPrivate()
+{
+ redirect.rt.reset(rhi, renderer);
+ if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
+ service->removeWindow(q_func());
+}
+
+void QQuickWindowPrivate::updateChildrenPalettes(const QPalette &parentPalette)
+{
+ Q_Q(QQuickWindow);
+ if (auto root = q->contentItem()) {
+ for (auto &&child: root->childItems()) {
+ QQuickItemPrivate::get(child)->inheritPalette(parentPalette);
+ }
+ }
+}
+
+void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
+{
+ q_ptr = c;
+
+
+ Q_Q(QQuickWindow);
+
+ contentItem = new QQuickRootItem;
+ QQml_setParent_noEvent(contentItem, c);
+ QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
+ QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem);
+ contentItemPrivate->window = q;
+ contentItemPrivate->windowRefCount = 1;
+ contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
+ contentItem->setSize(q->size());
+
+ visualizationMode = qgetenv("QSG_VISUALIZE");
+ renderControl = control;
+ if (renderControl)
+ QQuickRenderControlPrivate::get(renderControl)->window = q;
+
+ if (!renderControl)
+ windowManager = QSGRenderLoop::instance();
+
+ Q_ASSERT(windowManager || renderControl);
+
+ if (QScreen *screen = q->screen())
+ devicePixelRatio = screen->devicePixelRatio();
+
+ QSGContext *sg;
+ if (renderControl) {
+ QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl);
+ sg = renderControlPriv->sg;
+ context = renderControlPriv->rc;
+ } else {
+ windowManager->addWindow(q);
+ sg = windowManager->sceneGraphContext();
+ context = windowManager->createRenderContext(sg);
+ }
+
+ q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
+ q->setFormat(sg->defaultSurfaceFormat());
+ // When using Vulkan, associating a scenegraph-managed QVulkanInstance with
+ // the window (but only when not using renderControl) is deferred to
+ // QSGRhiSupport::createRhi(). This allows applications to set up their own
+ // QVulkanInstance and set that on the window, if they wish to.
+
+ animationController.reset(new QQuickAnimatorController(q));
+
+ QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
+ QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
+ QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
+
+ QObject::connect(q, SIGNAL(focusObjectChanged(QObject*)), q, SIGNAL(activeFocusItemChanged()));
+ QObject::connect(q, SIGNAL(screenChanged(QScreen*)), q, SLOT(handleScreenChanged(QScreen*)));
+ QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
+ q, SLOT(handleApplicationStateChanged(Qt::ApplicationState)));
+ QObject::connect(q, SIGNAL(frameSwapped()), q, SLOT(runJobsAfterSwap()), Qt::DirectConnection);
+
+ if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
+ service->addWindow(q);
+}
+
+void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state)
+{
+ Q_D(QQuickWindow);
+ if (state != Qt::ApplicationActive && d->contentItem)
+ d->handleWindowDeactivate();
+}
+
+/*!
+ \property QQuickWindow::data
+ \internal
+*/
+
+QQmlListProperty<QObject> QQuickWindowPrivate::data()
+{
+ return QQmlListProperty<QObject>(q_func(), nullptr,
+ QQuickWindowPrivate::data_append,
+ QQuickWindowPrivate::data_count,
+ QQuickWindowPrivate::data_at,
+ QQuickWindowPrivate::data_clear,
+ QQuickWindowPrivate::data_replace,
+ QQuickWindowPrivate::data_removeLast);
+}
+
+void QQuickWindowPrivate::touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent)
+{
+ Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
+ QMutableSinglePointEvent ret(type, touchEvent->pointingDevice(), p,
+ (type == QEvent::MouseMove ? Qt::NoButton : Qt::LeftButton),
+ (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton),
+ touchEvent->modifiers(), Qt::MouseEventSynthesizedByQt);
+ ret.setAccepted(true); // this now causes the persistent touchpoint to be accepted too
+ *mouseEvent = ret;
+}
+
+bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
+{
+ bool doubleClicked = false;
+
+ if (touchMousePressTimestamp > 0) {
+ QPoint distanceBetweenPresses = newPressPos - touchMousePressPos;
+ const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance();
+ doubleClicked = (qAbs(distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(distanceBetweenPresses.y()) <= doubleTapDistance);
+
+ if (doubleClicked) {
+ ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp;
+ ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->
+ mouseDoubleClickInterval());
+ doubleClicked = timeBetweenPresses < doubleClickInterval;
+ }
+ }
+ if (doubleClicked) {
+ touchMousePressTimestamp = 0;
+ } else {
+ touchMousePressTimestamp = newPressEventTimestamp;
+ touchMousePressPos = newPressPos;
+ }
+
+ return doubleClicked;
+}
+
+QPointerEvent *QQuickWindowPrivate::eventInDelivery() const
+{
+ if (eventsInDelivery.isEmpty())
+ return nullptr;
+ return eventsInDelivery.top();
+}
+
+/*! \internal
+ A helper function for the benefit of obsolete APIs like QQuickItem::grabMouse()
+ that don't have the currently-being-delivered event in context.
+ Returns the device the currently-being-delivered event comse from.
+*/
+QPointingDevicePrivate::EventPointData *QQuickWindowPrivate::mousePointData()
+{
+ if (eventsInDelivery.isEmpty())
+ return nullptr;
+ auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(eventsInDelivery.top()->pointingDevice()));
+ return devPriv->pointById(isDeliveringTouchAsMouse() ? touchMouseId : 0);
+}
+
+void QQuickWindowPrivate::cancelTouchMouseSynthesis()
+{
+ qCDebug(lcTouchTarget) << "id" << touchMouseId << "on" << touchMouseDevice;
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
+}
+
+bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent)
+{
+ Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
+ auto device = pointerEvent->pointingDevice();
+
+ // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant
+ if (device->type() == QInputDevice::DeviceType::TouchPad && device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) {
+ qCDebug(lcTouchTarget) << "skipping delivery of synth-mouse event from" << device;
+ return false;
+ }
+
+ // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
+ QMutableTouchEvent event;
+ QQuickItemPrivate::get(item)->localizedTouchEvent(pointerEvent, false, &event);
+ if (!event.points().count())
+ return false;
+
+ // For each point, check if it is accepted, if not, try the next point.
+ // Any of the fingers can become the mouse one.
+ // This can happen because a mouse area might not accept an event at some point but another.
+ for (auto &p : event.points()) {
+ // A new touch point
+ if (touchMouseId == -1 && p.state() & QEventPoint::State::Pressed) {
+ QPointF pos = item->mapFromScene(p.scenePosition());
+
+ // probably redundant, we check bounds in the calling function (matchingNewPoints)
+ if (!item->contains(pos))
+ break;
+
+ qCDebug(lcTouchTarget) << device << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
+ QMutableSinglePointEvent mousePress;
+ touchToMouseEvent(QEvent::MouseButtonPress, p, &event, &mousePress);
+
+ // Send a single press and see if that's accepted
+ QCoreApplication::sendEvent(item, &mousePress);
+ event.setAccepted(mousePress.isAccepted());
+ if (mousePress.isAccepted()) {
+ touchMouseDevice = device;
+ touchMouseId = p.id();
+ const auto &pt = mousePress.point(0);
+ if (!mousePress.exclusiveGrabber(pt))
+ mousePress.setExclusiveGrabber(pt, item);
+
+ if (checkIfDoubleTapped(event.timestamp(), p.globalPosition().toPoint())) {
+ // since we synth the mouse event from from touch, we respect the
+ // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance
+ QMutableSinglePointEvent mouseDoubleClick;
+ touchToMouseEvent(QEvent::MouseButtonDblClick, p, &event, &mouseDoubleClick);
+ QCoreApplication::sendEvent(item, &mouseDoubleClick);
+ event.setAccepted(mouseDoubleClick.isAccepted());
+ if (!mouseDoubleClick.isAccepted())
+ cancelTouchMouseSynthesis();
+ }
+
+ return true;
+ }
+ // try the next point
+
+ // Touch point was there before and moved
+ } else if (touchMouseDevice == device && p.id() == touchMouseId) {
+ if (p.state() & QEventPoint::State::Updated) {
+ if (touchMousePressTimestamp != 0) {
+ const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
+ const QPoint moveDelta = p.globalPosition().toPoint() - touchMousePressPos;
+ if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance)
+ touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap
+ }
+ if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) {
+ QMutableSinglePointEvent me;
+ touchToMouseEvent(QEvent::MouseMove, p, &event, &me);
+ QCoreApplication::sendEvent(item, &me);
+ event.setAccepted(me.isAccepted());
+ if (me.isAccepted())
+ qCDebug(lcTouchTarget) << device << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
+ return event.isAccepted();
+ } else {
+ // no grabber, check if we care about mouse hover
+ // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
+ // hover for touch???
+ QMutableSinglePointEvent me;
+ touchToMouseEvent(QEvent::MouseMove, p, &event, &me);
+ if (lastMousePosition.isNull())
+ lastMousePosition = me.scenePosition();
+ QPointF last = lastMousePosition;
+ lastMousePosition = me.scenePosition();
+
+ bool accepted = me.isAccepted();
+ bool delivered = deliverHoverEvent(contentItem, me.scenePosition(), last, me.modifiers(), me.timestamp(), accepted);
+ // take care of any exits
+ if (!delivered)
+ clearHover(me.timestamp());
+ break;
+ }
+ } else if (p.state() & QEventPoint::State::Released) {
+ // currently handled point was released
+ if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) {
+ QMutableSinglePointEvent me;
+ touchToMouseEvent(QEvent::MouseButtonRelease, p, &event, &me);
+ QCoreApplication::sendEvent(item, &me);
+
+ if (item->acceptHoverEvents() && p.globalPosition() != QGuiApplicationPrivate::lastCursorPosition) {
+ QPointF localMousePos(qInf(), qInf());
+ if (QWindow *w = item->window())
+ localMousePos = item->mapFromScene(w->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint()));
+ QMouseEvent mm(QEvent::MouseMove, localMousePos, QGuiApplicationPrivate::lastCursorPosition,
+ Qt::NoButton, Qt::NoButton, event.modifiers());
+ QCoreApplication::sendEvent(item, &mm);
+ }
+ if (pointerEvent->exclusiveGrabber(p) == mouseGrabberItem) // might have ungrabbed due to event
+ pointerEvent->setExclusiveGrabber(p, nullptr);
+
+ cancelTouchMouseSynthesis();
+ return me.isAccepted();
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+/*!
+ Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber.
+ This should not be called when processing a release event - that's redundant.
+ It is called in other cases, when the points may not be released, but the item
+ nevertheless must lose its grab due to becoming disabled, invisible, etc.
+ QPointerEvent::setExclusiveGrabber() calls touchUngrabEvent() when all points are released,
+ but if not all points are released, it cannot be sure whether to call touchUngrabEvent()
+ or not; so we have to do it here.
+*/
+void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch, bool cancel)
+{
+ Q_Q(QQuickWindow);
+ if (eventsInDelivery.isEmpty()) {
+ // do it the expensive way
+ for (auto dev : knownPointingDevices) {
+ auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(dev));
+ devPriv->removeGrabber(grabber, cancel);
+ }
+ return;
+ }
+ auto eventInDelivery = eventsInDelivery.top();
+ if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber && eventInDelivery) {
+ const bool fromTouch = isDeliveringTouchAsMouse();
+ auto point = eventInDelivery->pointById(fromTouch ? touchMouseId : 0);
+ Q_ASSERT(point);
+ QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(eventInDelivery->exclusiveGrabber(*point));
+ qCDebug(lcMouseTarget) << "removeGrabber" << oldGrabber << "-> null";
+ eventInDelivery->setExclusiveGrabber(*point, nullptr);
+ }
+ if (Q_LIKELY(touch)) {
+ bool ungrab = false;
+ const auto touchDevices = QPointingDevice::devices();
+ for (auto device : touchDevices) {
+ if (device->type() != QInputDevice::DeviceType::TouchScreen)
+ continue;
+ if (QPointingDevicePrivate::get(const_cast<QPointingDevice *>(static_cast<const QPointingDevice *>(device)))->
+ removeExclusiveGrabber(eventInDelivery, grabber))
+ ungrab = true;
+ }
+ if (ungrab)
+ grabber->touchUngrabEvent();
+ }
+}
+
+/*! \internal
+ Translates QEventPoint::scenePosition() in \a touchEvent to this window.
+
+ The item-local QEventPoint::position() is updated later, not here.
+*/
+void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
+{
+ for (qsizetype i = 0; i != touchEvent->pointCount(); ++i) {
+ auto &pt = QMutableEventPoint::from(touchEvent->point(i));
+ pt.setScenePosition(pt.position());
+ }
+}
+
+
+static inline bool windowHasFocus(QQuickWindow *win)
+{
+ const QWindow *focusWindow = QGuiApplication::focusWindow();
+ return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow;
+}
+
+#ifdef Q_OS_WEBOS
+// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
+static inline bool singleWindowOnScreen(QQuickWindow *win)
+{
+ const QWindowList windowList = QGuiApplication::allWindows();
+ for (int i = 0; i < windowList.count(); i++) {
+ QWindow *ii = windowList.at(i);
+ if (ii == win)
+ continue;
+ if (ii->screen() == win->screen())
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+/*!
+Set the focus inside \a scope to be \a item.
+If the scope contains the active focus item, it will be changed to \a item.
+Calls notifyFocusChangesRecur for all changed items.
+*/
+void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
+{
+ Q_Q(QQuickWindow);
+
+ Q_ASSERT(item);
+ Q_ASSERT(scope || item == contentItem);
+
+ qCDebug(lcFocus) << "QQuickWindowPrivate::setFocusInScope():";
+ qCDebug(lcFocus) << " scope:" << (QObject *)scope;
+ if (scope)
+ qCDebug(lcFocus) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
+ qCDebug(lcFocus) << " item:" << (QObject *)item;
+ qCDebug(lcFocus) << " activeFocusItem:" << (QObject *)activeFocusItem;
+
+ QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : nullptr;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+
+ QQuickItem *oldActiveFocusItem = nullptr;
+ QQuickItem *currentActiveFocusItem = activeFocusItem;
+ QQuickItem *newActiveFocusItem = nullptr;
+ bool sendFocusIn = false;
+
+ lastFocusReason = reason;
+
+ QVarLengthArray<QQuickItem *, 20> changed;
+
+ // Does this change the active focus?
+ if (item == contentItem || scopePrivate->activeFocus) {
+ oldActiveFocusItem = activeFocusItem;
+ if (item->isEnabled()) {
+ newActiveFocusItem = item;
+ while (newActiveFocusItem->isFocusScope()
+ && newActiveFocusItem->scopedFocusItem()
+ && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
+ newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
+ }
+ } else {
+ newActiveFocusItem = scope;
+ }
+
+ if (oldActiveFocusItem) {
+#if QT_CONFIG(im)
+ QGuiApplication::inputMethod()->commit();
+#endif
+
+ activeFocusItem = nullptr;
+
+ QQuickItem *afi = oldActiveFocusItem;
+ while (afi && afi != scope) {
+ if (QQuickItemPrivate::get(afi)->activeFocus) {
+ QQuickItemPrivate::get(afi)->activeFocus = false;
+ changed << afi;
+ }
+ afi = afi->parentItem();
+ }
+ }
+ }
+
+ if (item != contentItem && !(options & DontChangeSubFocusItem)) {
+ QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
+ if (oldSubFocusItem) {
+ QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
+ changed << oldSubFocusItem;
+ }
+
+ QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
+ }
+
+ if (!(options & DontChangeFocusProperty)) {
+ if (item != contentItem
+ || windowHasFocus(q)
+#ifdef Q_OS_WEBOS
+ // Allow focused if there is only one window in the screen where it belongs.
+ // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
+ || singleWindowOnScreen(q)
+#endif
+ ) {
+ itemPrivate->focus = true;
+ changed << item;
+ }
+ }
+
+ if (newActiveFocusItem && contentItem->hasFocus()) {
+ activeFocusItem = newActiveFocusItem;
+
+ QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
+ changed << newActiveFocusItem;
+
+ QQuickItem *afi = newActiveFocusItem->parentItem();
+ while (afi && afi != scope) {
+ if (afi->isFocusScope()) {
+ QQuickItemPrivate::get(afi)->activeFocus = true;
+ changed << afi;
+ }
+ afi = afi->parentItem();
+ }
+ updateFocusItemTransform();
+ sendFocusIn = true;
+ }
+
+ // Now that all the state is changed, emit signals & events
+ // We must do this last, as this process may result in further changes to focus.
+ if (oldActiveFocusItem) {
+ QFocusEvent event(QEvent::FocusOut, reason);
+ QCoreApplication::sendEvent(oldActiveFocusItem, &event);
+ }
+
+ // Make sure that the FocusOut didn't result in another focus change.
+ if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
+ QFocusEvent event(QEvent::FocusIn, reason);
+ QCoreApplication::sendEvent(newActiveFocusItem, &event);
+ }
+
+ if (activeFocusItem != currentActiveFocusItem)
+ emit q->focusObjectChanged(activeFocusItem);
+
+ if (!changed.isEmpty())
+ notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+}
+
+void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
+{
+ Q_Q(QQuickWindow);
+
+ Q_ASSERT(item);
+ Q_ASSERT(scope || item == contentItem);
+
+ qCDebug(lcFocus) << "QQuickWindowPrivate::clearFocusInScope():";
+ qCDebug(lcFocus) << " scope:" << (QObject *)scope;
+ qCDebug(lcFocus) << " item:" << (QObject *)item;
+ qCDebug(lcFocus) << " activeFocusItem:" << (QObject *)activeFocusItem;
+
+ QQuickItemPrivate *scopePrivate = nullptr;
+ if (scope) {
+ scopePrivate = QQuickItemPrivate::get(scope);
+ if ( !scopePrivate->subFocusItem )
+ return;//No focus, nothing to do.
+ }
+
+ QQuickItem *currentActiveFocusItem = activeFocusItem;
+ QQuickItem *oldActiveFocusItem = nullptr;
+ QQuickItem *newActiveFocusItem = nullptr;
+
+ lastFocusReason = reason;
+
+ QVarLengthArray<QQuickItem *, 20> changed;
+
+ Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);
+
+ // Does this change the active focus?
+ if (item == contentItem || scopePrivate->activeFocus) {
+ oldActiveFocusItem = activeFocusItem;
+ newActiveFocusItem = scope;
+
+#if QT_CONFIG(im)
+ QGuiApplication::inputMethod()->commit();
+#endif
+
+ activeFocusItem = nullptr;
+
+ if (oldActiveFocusItem) {
+ QQuickItem *afi = oldActiveFocusItem;
+ while (afi && afi != scope) {
+ if (QQuickItemPrivate::get(afi)->activeFocus) {
+ QQuickItemPrivate::get(afi)->activeFocus = false;
+ changed << afi;
+ }
+ afi = afi->parentItem();
+ }
+ }
+ }
+
+ if (item != contentItem && !(options & DontChangeSubFocusItem)) {
+ QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
+ if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
+ QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
+ changed << oldSubFocusItem;
+ }
+
+ QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
+
+ } else if (!(options & DontChangeFocusProperty)) {
+ QQuickItemPrivate::get(item)->focus = false;
+ changed << item;
+ }
+
+ if (newActiveFocusItem) {
+ Q_ASSERT(newActiveFocusItem == scope);
+ activeFocusItem = scope;
+ updateFocusItemTransform();
+ }
+
+ // Now that all the state is changed, emit signals & events
+ // We must do this last, as this process may result in further changes to
+ // focus.
+ if (oldActiveFocusItem) {
+ QFocusEvent event(QEvent::FocusOut, reason);
+ QCoreApplication::sendEvent(oldActiveFocusItem, &event);
+ }
+
+ // Make sure that the FocusOut didn't result in another focus change.
+ if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
+ QFocusEvent event(QEvent::FocusIn, reason);
+ QCoreApplication::sendEvent(newActiveFocusItem, &event);
+ }
+
+ if (activeFocusItem != currentActiveFocusItem)
+ emit q->focusObjectChanged(activeFocusItem);
+
+ if (!changed.isEmpty())
+ notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+}
+
+void QQuickWindowPrivate::clearFocusObject()
+{
+ if (activeFocusItem == contentItem)
+ return;
+
+ clearFocusInScope(contentItem, QQuickItemPrivate::get(contentItem)->subFocusItem, Qt::OtherFocusReason);
+}
+
+void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
+{
+ QPointer<QQuickItem> item(*items);
+
+ if (remaining)
+ notifyFocusChangesRecur(items + 1, remaining - 1);
+
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+
+ if (itemPrivate->notifiedFocus != itemPrivate->focus) {
+ itemPrivate->notifiedFocus = itemPrivate->focus;
+ emit item->focusChanged(itemPrivate->focus);
+ }
+
+ if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
+ itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
+ itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
+ emit item->activeFocusChanged(itemPrivate->activeFocus);
+ }
+ }
+}
+
+void QQuickWindowPrivate::dirtyItem(QQuickItem *)
+{
+ Q_Q(QQuickWindow);
+ q->maybeUpdate();
+}
+
+void QQuickWindowPrivate::cleanup(QSGNode *n)
+{
+ Q_Q(QQuickWindow);
+
+ Q_ASSERT(!cleanupNodeList.contains(n));
+ cleanupNodeList.append(n);
+ q->maybeUpdate();
+}
+
+/*!
+ \qmltype Window
+ \instantiates QQuickWindow
+ \inqmlmodule QtQuick.Window
+ \ingroup qtquick-visual
+ \brief Creates a new top-level window.
+
+ The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
+ window for use with \c {QtQuick 2.x} graphical types.
+
+ To use this type, you will need to import the module with the following line:
+ \code
+ import QtQuick.Window 2.2
+ \endcode
+
+ Omitting this import will allow you to have a QML environment without
+ access to window system features.
+
+ A Window can be declared inside an Item or inside another Window; in that
+ case the inner Window will automatically become "transient for" the outer
+ Window: that is, most platforms will show it centered upon the outer window
+ by default, and there may be other platform-dependent behaviors, depending
+ also on the \l flags. If the nested window is intended to be a dialog in
+ your application, you should also set \l flags to Qt.Dialog, because some
+ window managers will not provide the centering behavior without that flag.
+ You can also declare multiple windows inside a top-level \l QtObject, in which
+ case the windows will have no transient relationship.
+
+ Alternatively you can set or bind \l x and \l y to position the Window
+ explicitly on the screen.
+
+ When the user attempts to close a window, the \l closing signal will be
+ emitted. You can force the window to stay open (for example to prompt the
+ user to save changes) by writing an \c onClosing handler and setting
+ \c {close.accepted = false}.
+*/
+/*!
+ \class QQuickWindow
+ \since 5.0
+
+ \inmodule QtQuick
+
+ \brief The QQuickWindow class provides the window for displaying a graphical QML scene.
+
+ QQuickWindow provides the graphical scene management needed to interact with and display
+ a scene of QQuickItems.
+
+ A QQuickWindow always has a single invisible root item. To add items to this window,
+ reparent the items to the root item or to an existing item in the scene.
+
+ For easily displaying a scene from a QML file, see \l{QQuickView}.
+
+ \section1 Rendering
+
+ QQuickWindow uses a scene graph to represent what needs to be rendered.
+ This scene graph is disconnected from the QML scene and potentially lives in
+ another thread, depending on the platform implementation. Since the
+ rendering scene graph lives independently from the QML scene, it can also be
+ completely released without affecting the state of the QML scene.
+
+ The sceneGraphInitialized() signal is emitted on the rendering thread before
+ the QML scene is rendered to the screen for the first time. If the rendering
+ scene graph has been released, the signal will be emitted again before the
+ next frame is rendered. A visible, on-screen QQuickWindow is driven
+ internally by a \c{render loop}, of which there are multiple implementations
+ provided in the scene graph. For details on the scene graph rendering
+ process, see \l{Qt Quick Scene Graph}.
+
+ By default, a QQuickWindow renders using an accelerated 3D graphics API,
+ such as OpenGL or Vulkan. See \l{Scene Graph Adaptations} for a detailed
+ overview of scene graph backends and the supported graphics APIs.
+
+ \warning It is crucial that graphics operations and interaction with the
+ scene graph happens exclusively on the rendering thread, primarily during
+ the updatePaintNode() phase.
+
+ \warning As many of the signals related to rendering are emitted from the
+ rendering thread, connections should be made using Qt::DirectConnection.
+
+ \section2 Integration with Accelerated 3D Graphics APIs
+
+ It is possible to integrate OpenGL, Vulkan, Metal, or Direct3D 11 calls
+ directly into the QQuickWindow, as long as the QQuickWindow and the
+ underlying scene graph is rendering using the same API. To access native
+ graphics objects, such as device or context object handles, use
+ QSGRendererInterface. An instance of QSGRendererInterface is queriable from
+ QQuickWindow by calling rendererInterface(). The enablers for this
+ integration are the beforeRendering(), beforeRenderPassRecording(),
+ afterRenderPassRecording(), and related signals. These allow rendering
+ underlays or overlays. Alternatively, QNativeInterface::QSGOpenGLTexture,
+ QNativeInterface::QSGVulkanTexture, and other similar classes allow
+ wrapping an existing native texture or image object in a QSGTexture that
+ can then be used with the scene graph.
+
+ \section2 Rendering without Acceleration
+
+ A limited, pure software based rendering path is available as well. With the
+ \c software backend, a number of Qt Quick features are not available, QML
+ items relying on these will not be rendered at all. At the same time, this
+ allows QQuickWindow to be functional even on systems where there is no 3D
+ graphics API available at all. See \l{Qt Quick Software Adaptation} for more
+ details.
+
+ \section2 Redirected Rendering
+
+ A QQuickWindow is not necessarily backed by a native window on screen. The
+ rendering can be redirected to target a custom render target, such as a
+ given native texture. This is achieved in combination with the
+ QQuickRenderControl class, and functions such as setRenderTarget(),
+ setGraphicsDevice(), and setGraphicsConfiguration().
+
+ In this case, the QQuickWindow represents the scene, and provides the
+ intrastructure for rendering a frame. It will not be backed by a render
+ loop and a native window. Instead, in this case the application drives
+ rendering, effectively substituting for the render loops. This allows
+ generating image sequences, rendering into textures for use in external 3D
+ engines, or rendering Qt Quick content within a VR environment.
+
+ \section2 Resource Management
+
+ QML will try to cache images and scene graph nodes to improve performance,
+ but in some low-memory scenarios it might be required to aggressively
+ release these resources. The releaseResources() function can be used to
+ force the clean up of certain resources, especially resource that are cached
+ and can be recreated later when needed again.
+
+ Additionally, calling releaseResources() may result in releasing the entire
+ scene graph and the associated graphics resources. The
+ sceneGraphInvalidated() signal will be emitted when this happens. This
+ behavior is controlled by the setPersistentGraphics() and
+ setPersistentSceneGraph() functions.
+
+ \note All classes with QSG prefix should be used solely on the scene graph's
+ rendering thread. See \l {Scene Graph and Rendering} for more information.
+
+ \section2 Exposure and Visibility
+
+ When a QQuickWindow instance is deliberately hidden with hide() or
+ setVisible(false), it will stop rendering and its scene graph and graphics
+ context might be released as well. This depends on the settings configured
+ by setPersistentGraphics() and setPersistentSceneGraph(). The behavior in
+ this respect is identical to explicitly calling the releaseResources()
+ function. A window can become not exposed, in other words non-renderable, by
+ other means as well. This depends on the platform and windowing system. For
+ example, on Windows minimizing a window makes it stop rendering. On \macos
+ fully obscuring a window by other windows on top triggers the same. On
+ Linux/X11, the behavior is dependent on the window manager.
+
+ \section2 OpenGL Context and Surface Formats
+
+ While it is possible to specify a QSurfaceFormat for every QQuickWindow by
+ calling the member function setFormat(), windows may also be created from
+ QML by using the Window and ApplicationWindow elements. In this case there
+ is no C++ code involved in the creation of the window instance, yet
+ applications may still wish to set certain surface format values, for
+ example to request a given OpenGL version or profile. Such applications can
+ call the static function QSurfaceFormat::setDefaultFormat() at startup. The
+ specified format will be used for all Quick windows created afterwards.
+
+ \section2 Vulkan Instance
+
+ When using Vulkan, a QQuickWindow is automatically associated with a
+ QVulkanInstance that is created and managed internally by the scene graph.
+ This way most applications do not need to worry about having a \c
+ VkInstance available since it all happens automatically. In advanced cases
+ an application may wish to create its own QVulkanInstance, in order to
+ configure it in a specific way. That is possible as well. Calling
+ \l{QWindow::setVulkanInstance()}{setVulkanInstance()} on the QQuickWindow
+ right after construction, before making it visible, leads to using the
+ application-supplied QVulkanInstance (and the underlying \c VkInstance).
+ When redirecting via QQuickRenderControl, there is no QVulkanInstance
+ provided automatically, but rather the application is expected to provide
+ its own and associate it with the QQuickWindow.
+
+ \section2 Graphics Contexts and Devices
+
+ When the scene graph is initialized, which typically happens when the
+ window becomes exposed or, in case of redirected rendering, initialization
+ is performed \l{QQuickRenderControl::initialize()}{via
+ QQuickRenderControl}, the context or device objects necessary for rendering
+ are created automatically. This includes OpenGL contexts, Direct3D devices
+ and device contexts, Vulkan and Metal devices. These are also queriable by
+ application code afterwards via
+ \l{QSGRendererInterface::getResource()}{QSGRendererInterface}. When using
+ the \c basic render loop, which performs all rendering on the GUI thread,
+ the same context or device is used with all visible QQuickWindows. The \c
+ threaded render loop uses a dedicated context or device object for each
+ rendering thread, and so for each QQuickWindow. With some graphics APIs,
+ there is a degree of customizability provided via
+ setGraphicsConfiguration(). This makes it possible, for example, to specify
+ the list of Vulkan extensions to enable on the \c VkDevice. Alternatively,
+ it is also possible to provide a set of existing context or device objects
+ for use by the QQuickWindow, instead of letting it construct its own. This
+ is achieved through setGraphicsDevice().
+
+ \sa QQuickView, QQuickRenderControl, QQuickRenderTarget,
+ QQuickGraphicsDevice, QQuickGraphicsConfiguration, QSGRendererInterface
+*/
+
+/*!
+ Constructs a window for displaying a QML scene with parent window \a parent.
+*/
+QQuickWindow::QQuickWindow(QWindow *parent)
+ : QQuickWindow(*new QQuickWindowPrivate, parent)
+{
+}
+
+
+
+/*!
+ \internal
+*/
+QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
+ : QWindow(dd, parent)
+{
+ Q_D(QQuickWindow);
+ d->init(this);
+}
+
+/*!
+ \internal
+*/
+QQuickWindow::QQuickWindow(QQuickRenderControl *control)
+ : QWindow(*(new QQuickWindowPrivate), nullptr)
+{
+ Q_D(QQuickWindow);
+ d->init(this, control);
+}
+
+/*!
+ \internal
+*/
+QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
+ : QWindow(dd, nullptr)
+{
+ Q_D(QQuickWindow);
+ d->init(this, control);
+}
+
+/*!
+ Destroys the window.
+*/
+QQuickWindow::~QQuickWindow()
+{
+ Q_D(QQuickWindow);
+
+ if (d->renderControl) {
+ QQuickRenderControlPrivate::get(d->renderControl)->windowDestroyed();
+ } else if (d->windowManager) {
+ d->windowManager->removeWindow(this);
+ d->windowManager->windowDestroyed(this);
+ }
+
+ delete d->incubationController; d->incubationController = nullptr;
+#if QT_CONFIG(quick_draganddrop)
+ delete d->dragGrabber; d->dragGrabber = nullptr;
+#endif
+ QQuickRootItem *root = d->contentItem;
+ d->contentItem = nullptr;
+ delete root;
+
+ d->renderJobMutex.lock();
+ qDeleteAll(d->beforeSynchronizingJobs);
+ d->beforeSynchronizingJobs.clear();
+ qDeleteAll(d->afterSynchronizingJobs);
+ d->afterSynchronizingJobs.clear();
+ qDeleteAll(d->beforeRenderingJobs);
+ d->beforeRenderingJobs.clear();
+ qDeleteAll(d->afterRenderingJobs);
+ d->afterRenderingJobs.clear();
+ qDeleteAll(d->afterSwapJobs);
+ d->afterSwapJobs.clear();
+ d->renderJobMutex.unlock();
+
+ // It is important that the pixmap cache is cleaned up during shutdown.
+ // Besides playing nice, this also solves a practical problem that
+ // QQuickTextureFactory implementations in other libraries need
+ // have their destructors loaded while they the library is still
+ // loaded into memory.
+ QQuickPixmap::purgeCache();
+}
+
+#if QT_CONFIG(quick_shadereffect)
+void qtquick_shadereffect_purge_gui_thread_shader_cache();
+#endif
+
+/*!
+ This function tries to release redundant resources currently held by the QML scene.
+
+ Calling this function requests the scene graph to release cached graphics
+ resources, such as graphics pipeline objects or shader programs.
+
+ \note The releasing of cached graphics resources is not dependent on the
+ hint from setPersistentGraphics().
+
+ Additionally, depending on the render loop in use, this function may also
+ result in the scene graph and all rendering resources to be released. If
+ this happens, the sceneGraphInvalidated() signal will be emitted, allowing
+ users to clean up their own graphics resources. The
+ setPersistentGraphics() and setPersistentSceneGraph() functions can be
+ used to prevent this from happening, if handling the cleanup is not feasible
+ in the application, at the cost of higher memory usage.
+
+ \sa sceneGraphInvalidated(), setPersistentGraphics(), setPersistentSceneGraph()
+ */
+
+void QQuickWindow::releaseResources()
+{
+ Q_D(QQuickWindow);
+ if (d->windowManager)
+ d->windowManager->releaseResources(this);
+ QQuickPixmap::purgeCache();
+#if QT_CONFIG(quick_shadereffect)
+ qtquick_shadereffect_purge_gui_thread_shader_cache();
+#endif
+}
+
+
+
+/*!
+ Sets whether the graphics resources (graphics device or context,
+ swapchain, buffers, textures) should be preserved, and cannot be
+ released until the last window is deleted, to \a persistent. The
+ default value is true.
+
+ When calling releaseResources(), or when the window gets hidden (more
+ specifically, not renderable), some render loops have the possibility
+ to release all, not just the cached, graphics resources. This can free
+ up memory temporarily, but it also means the rendering engine will have
+ to do a full, potentially costly reinitialization of the resources when
+ the window needs to render again.
+
+ \note The rules for when a window is not renderable are platform and
+ window manager specific.
+
+ \note All graphics resources are released when the last QQuickWindow is
+ deleted, regardless of this setting.
+
+ \note This is a hint, and is not guaranteed that it is taken into account.
+
+ \note This hint does not apply to cached resources, that are relatively
+ cheap to drop and then recreate later. Therefore, calling releaseResources()
+ will typically lead to releasing those regardless of the value of this hint.
+
+ \sa setPersistentSceneGraph(), sceneGraphInitialized(), sceneGraphInvalidated(), releaseResources()
+ */
+
+void QQuickWindow::setPersistentGraphics(bool persistent)
+{
+ Q_D(QQuickWindow);
+ d->persistentGraphics = persistent;
+}
+
+
+
+/*!
+ Returns whether essential graphics resources can be released during the
+ lifetime of the QQuickWindow.
+
+ \note This is a hint, and is not guaranteed that it is taken into account.
+
+ \sa setPersistentGraphics()
+ */
+
+bool QQuickWindow::isPersistentGraphics() const
+{
+ Q_D(const QQuickWindow);
+ return d->persistentGraphics;
+}
+
+
+
+/*!
+ Sets whether the scene graph nodes and resources are \a persistent.
+ Persistent means the nodes and resources cannot be released.
+ The default value is \c true.
+
+ When calling releaseResources(), when the window gets hidden (more
+ specifically, not renderable), some render loops have the possibility
+ to release the scene graph nodes and related graphics resources. This
+ frees up memory temporarily, but will also mean the scene graph has to
+ be rebuilt when the window renders next time.
+
+ \note The rules for when a window is not renderable are platform and
+ window manager specific.
+
+ \note The scene graph nodes and resources are always released when the
+ last QQuickWindow is deleted, regardless of this setting.
+
+ \note This is a hint, and is not guaranteed that it is taken into account.
+
+ \sa setPersistentGraphics(), sceneGraphInvalidated(), sceneGraphInitialized(), releaseResources()
+ */
+
+void QQuickWindow::setPersistentSceneGraph(bool persistent)
+{
+ Q_D(QQuickWindow);
+ d->persistentSceneGraph = persistent;
+}
+
+
+
+/*!
+ Returns whether the scene graph nodes and resources can be
+ released during the lifetime of this QQuickWindow.
+
+ \note This is a hint. When and how this happens is implementation
+ specific.
+ */
+
+bool QQuickWindow::isPersistentSceneGraph() const
+{
+ Q_D(const QQuickWindow);
+ return d->persistentSceneGraph;
+}
+
+
+
+
+/*!
+ \qmlattachedproperty Item Window::contentItem
+ \since 5.4
+
+ This attached property holds the invisible root item of the scene or
+ \c null if the item is not in a window. The Window attached property
+ can be attached to any Item.
+*/
+
+/*!
+ \property QQuickWindow::contentItem
+ \brief The invisible root item of the scene.
+
+ A QQuickWindow always has a single invisible root item containing all of its content.
+ To add items to this window, reparent the items to the contentItem or to an existing
+ item in the scene.
+*/
+QQuickItem *QQuickWindow::contentItem() const
+{
+ Q_D(const QQuickWindow);
+
+ return d->contentItem;
+}
+
+/*!
+ \property QQuickWindow::activeFocusItem
+
+ \brief The item which currently has active focus or \c null if there is
+ no item with active focus.
+*/
+QQuickItem *QQuickWindow::activeFocusItem() const
+{
+ Q_D(const QQuickWindow);
+
+ return d->activeFocusItem;
+}
+
+/*!
+ \internal
+ \reimp
+*/
+QObject *QQuickWindow::focusObject() const
+{
+ Q_D(const QQuickWindow);
+
+ if (d->activeFocusItem)
+ return d->activeFocusItem;
+ return const_cast<QQuickWindow*>(this);
+}
+
+
+/*!
+ \obsolete Use QPointerEvent::exclusiveGrabber()
+ Returns the item which currently has the mouse grab.
+*/
+QQuickItem *QQuickWindow::mouseGrabberItem() const
+{
+ Q_D(const QQuickWindow);
+ auto epd = const_cast<QQuickWindowPrivate *>(d)->mousePointData();
+ if (!epd && d->eventsInDelivery.isEmpty()) {
+ qCDebug(lcMouse, "mouse grabber ambiguous: no event is currently being delivered");
+ return qmlobject_cast<QQuickItem *>(QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice())->
+ firstPointExclusiveGrabber());
+ }
+ return qobject_cast<QQuickItem *>(epd->exclusiveGrabber);
+}
+
+bool QQuickWindowPrivate::clearHover(ulong timestamp)
+{
+ Q_Q(QQuickWindow);
+ if (hoverItems.isEmpty())
+ return false;
+
+ QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+
+ bool accepted = false;
+ for (QQuickItem* item : qAsConst(hoverItems)) {
+ accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
+ }
+ hoverItems.clear();
+ return accepted;
+}
+
+/*! \reimp */
+bool QQuickWindow::event(QEvent *e)
+{
+ Q_D(QQuickWindow);
+
+ switch (e->type()) {
+
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd: {
+ QTouchEvent *touch = static_cast<QTouchEvent*>(e);
+ d->handleTouchEvent(touch);
+ if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
+ // we consume all touch events ourselves to avoid duplicate
+ // mouse delivery by QtGui mouse synthesis
+ e->accept();
+ }
+ return true;
+ }
+ break;
+ case QEvent::TouchCancel:
+ // return in order to avoid the QWindow::event below
+ return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
+ break;
+ case QEvent::Enter: {
+ if (!d->contentItem)
+ return false;
+ QEnterEvent *enter = static_cast<QEnterEvent*>(e);
+ bool accepted = enter->isAccepted();
+ bool delivered = d->deliverHoverEvent(d->contentItem, enter->scenePosition(), d->lastMousePosition,
+ QGuiApplication::keyboardModifiers(), 0L, accepted);
+ d->lastMousePosition = enter->scenePosition();
+ enter->setAccepted(accepted);
+#if QT_CONFIG(cursor)
+ d->updateCursor(mapFromGlobal(QCursor::pos()));
+#endif
+ return delivered;
+ }
+ break;
+ case QEvent::Leave:
+ d->clearHover();
+ d->lastMousePosition = QPointF();
+ break;
+#if QT_CONFIG(quick_draganddrop)
+ case QEvent::DragEnter:
+ case QEvent::DragLeave:
+ case QEvent::DragMove:
+ case QEvent::Drop:
+ d->deliverDragEvent(d->dragGrabber, e);
+ break;
+#endif
+ case QEvent::WindowDeactivate:
+ d->handleWindowDeactivate();
+ break;
+ case QEvent::PlatformSurface:
+ if ((static_cast<QPlatformSurfaceEvent *>(e))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
+ // Ensure that the rendering thread is notified before
+ // the QPlatformWindow is destroyed.
+ if (d->windowManager)
+ d->windowManager->hide(this);
+ }
+ break;
+ case QEvent::FocusAboutToChange:
+#if QT_CONFIG(im)
+ if (d->activeFocusItem)
+ qGuiApp->inputMethod()->commit();
+#endif
+ break;
+ case QEvent::UpdateRequest: {
+ if (d->windowManager)
+ d->windowManager->handleUpdateRequest(this);
+ break;
+ }
+#if QT_CONFIG(gestures)
+ case QEvent::NativeGesture:
+ d->deliverSinglePointEventUntilAccepted(static_cast<QPointerEvent *>(e));
+ break;
+#endif
+ case QEvent::ShortcutOverride:
+ if (d->activeFocusItem)
+ QCoreApplication::sendEvent(d->activeFocusItem, e);
+ return true;
+ case QEvent::LanguageChange:
+ if (d->contentItem)
+ QCoreApplication::sendEvent(d->contentItem, e);
+ break;
+ case QEvent::InputMethod:
+ case QEvent::InputMethodQuery:
+ {
+ QQuickItem *target = d->activeFocusItem;
+ // while an input method delivers the event, this window might still be inactive
+ if (!target) {
+ target = d->contentItem;
+ if (!target || !target->isEnabled())
+ break;
+ // see setFocusInScope for a similar loop
+ while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
+ target = target->scopedFocusItem();
+ }
+ if (target) {
+ QCoreApplication::sendEvent(target, e);
+ return true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (e->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))
+ update();
+ else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure))
+ d->windowManager->handleContextCreationFailure(this);
+
+ return QWindow::event(e);
+}
+
+/*! \reimp */
+void QQuickWindow::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QQuickWindow);
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(),
+ e->modifiers());
+ d->deliverKeyEvent(e);
+}
+
+/*! \reimp */
+void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
+{
+ Q_D(QQuickWindow);
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(),
+ e->modifiers());
+ d->deliverKeyEvent(e);
+}
+
+void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
+{
+ if (activeFocusItem) {
+ QQuickItem *item = activeFocusItem;
+
+ // In case of generated event, trigger ShortcutOverride event
+ if (e->type() == QEvent::KeyPress && e->spontaneous() == false)
+ qt_sendShortcutOverrideEvent(item, e->timestamp(),
+ e->key(), e->modifiers(), e->text(),
+ e->isAutoRepeat(), e->count());
+
+ e->accept();
+ QCoreApplication::sendEvent(item, e);
+ while (!e->isAccepted() && (item = item->parentItem())) {
+ e->accept();
+ QCoreApplication::sendEvent(item, e);
+ }
+ }
+}
+
+/*! \internal
+ Make a copy of any type of QPointerEvent, and optionally localize it
+ by setting its first point's local position() if \a transformedLocalPos is given.
+
+ \note some subclasses of QSinglePointEvent, such as QWheelEvent, add extra storage.
+ This function doesn't yet support cloning all of those; it can be extended if needed.
+*/
+QPointerEvent *QQuickWindowPrivate::clonePointerEvent(QPointerEvent *event, std::optional<QPointF> transformedLocalPos)
+{
+ QPointerEvent *ret = event->clone();
+ QMutableEventPoint &point = QMutableEventPoint::from(ret->point(0));
+ point.detach();
+ point.setTimestamp(event->timestamp());
+ if (transformedLocalPos)
+ point.setPosition(*transformedLocalPos);
+
+ return ret;
+}
+
+void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers,
+ QPointerEvent *pointerEvent)
+{
+ const QVector<QObject *> &eventDeliveryTargets =
+ QQuickPointerHandlerPrivate::deviceDeliveryTargets(pointerEvent->device());
+ QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
+ hasFiltered.clear();
+ for (auto o : passiveGrabbers) {
+ QQuickPointerHandler *handler = qobject_cast<QQuickPointerHandler *>(o);
+ // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
+ if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
+ bool alreadyFiltered = false;
+ QQuickItem *par = handler->parentItem();
+
+ // see if we already have sent a filter event to the parent
+ auto it = std::find_if(sendFilteredPointerEventResult.begin(), sendFilteredPointerEventResult.end(),
+ [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; });
+ if (it != sendFilteredPointerEventResult.end()) {
+ // Yes, the event was already filtered to that parent, do not call it again but use
+ // the result of the previous call to determine if we should call the handler.
+ alreadyFiltered = it->second;
+ } else {
+ alreadyFiltered = sendFilteredPointerEvent(pointerEvent, par);
+ sendFilteredPointerEventResult << qMakePair(par, alreadyFiltered);
+ }
+ if (!alreadyFiltered) {
+ localizePointerEvent(pointerEvent, handler->parentItem());
+ handler->handlePointerEvent(pointerEvent);
+ }
+ }
+ }
+}
+
+bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
+ const QPointF &scenePos, const QPointF &lastScenePos,
+ Qt::KeyboardModifiers modifiers, ulong timestamp,
+ bool accepted)
+{
+ const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
+
+ //create copy of event
+ QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
+ hoverEvent.setTimestamp(timestamp);
+ hoverEvent.setAccepted(accepted);
+
+ hasFiltered.clear();
+ if (sendFilteredMouseEvent(&hoverEvent, item, item->parentItem()))
+ return true;
+
+ QCoreApplication::sendEvent(item, &hoverEvent);
+
+ return hoverEvent.isAccepted();
+}
+
+// TODO later: specify the device in case of multi-mouse scenario, or mouse and tablet both in use
+bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
+ Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
+{
+ Q_Q(QQuickWindow);
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+
+ if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(scenePos);
+ if (!item->contains(p))
+ return false;
+ }
+
+ if (Q_UNLIKELY(lcHoverTrace().isDebugEnabled())) {
+ if (lastScenePos == scenePos)
+ qCDebug(lcHoverTrace) << scenePos << "(unchanged)" << item << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled << "in window" << windowTitle;
+ else
+ qCDebug(lcHoverTrace) << lastScenePos << "->" << scenePos << item << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled << "in window" << windowTitle;
+ }
+ if (itemPrivate->subtreeHoverEnabled) {
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QQuickItem *child = children.at(ii);
+ if (!child->isVisible() || QQuickItemPrivate::get(child)->culled)
+ continue;
+ if (!child->isEnabled() && !QQuickItemPrivate::get(child)->subtreeHoverEnabled)
+ continue;
+ if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, timestamp, accepted))
+ return true;
+ }
+ }
+
+ if (itemPrivate->hasPointerHandlers()) {
+ const QPointF localPos = item->mapFromScene(scenePos);
+ QMouseEvent hoverEvent(QEvent::MouseMove, localPos, scenePos, q->mapToGlobal(scenePos), Qt::NoButton, Qt::NoButton, modifiers);
+ hoverEvent.setTimestamp(timestamp);
+ hoverEvent.setAccepted(true);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(&hoverEvent);
+ }
+
+ if (itemPrivate->hoverEnabled) {
+ QPointF p = item->mapFromScene(scenePos);
+ if (item->contains(p)) {
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {
+ //move
+ accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ } else {
+ QList<QQuickItem *> itemsToHover;
+ QQuickItem* parent = item;
+ itemsToHover << item;
+ while ((parent = parent->parentItem()))
+ itemsToHover << parent;
+
+ // Leaving from previous hovered items until we reach the item or one of its ancestors.
+ while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems.at(0))) {
+ QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
+ sendHoverEvent(QEvent::HoverLeave, hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ }
+
+ if (!hoverItems.isEmpty() && hoverItems.at(0) == item) {//Not entering a new Item
+ // ### Shouldn't we send moves for the parent items as well?
+ accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ } else {
+ // Enter items that are not entered yet.
+ int startIdx = -1;
+ if (!hoverItems.isEmpty())
+ startIdx = itemsToHover.indexOf(hoverItems.at(0)) - 1;
+ if (startIdx == -1)
+ startIdx = itemsToHover.count() - 1;
+
+ for (int i = startIdx; i >= 0; i--) {
+ QQuickItem *itemToHover = itemsToHover.at(i);
+ QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(itemToHover);
+ // The item may be about to be deleted or reparented to another window
+ // due to another hover event delivered in this function. If that is the
+ // case, sending a hover event here will cause a crash or other bad
+ // behavior when the leave event is generated. Checking
+ // itemToHoverPrivate->window here prevents that case.
+ if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) {
+ hoverItems.prepend(itemToHover);
+ sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted);
+ }
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
+// in the usual reverse-paint-order until propagation is stopped
+bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QPointerEvent *event)
+{
+ Q_ASSERT(event->points().count() == 1);
+ QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->pointingDevice()).clear();
+ QEventPoint &point = event->point(0);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, event, point, false, false);
+ point.setAccepted(false);
+
+ for (QQuickItem *item : targetItems) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ localizePointerEvent(event, item);
+ // Let Pointer Handlers have the first shot
+ itemPrivate->handlePointerEvent(event);
+ if (point.isAccepted())
+ return true;
+ event->accept();
+ QCoreApplication::sendEvent(item, event);
+ if (event->isAccepted()) {
+ qCDebug(lcWheelTarget) << event << "->" << item;
+ return true;
+ }
+ }
+
+ return false; // it wasn't handled
+}
+
+#if QT_CONFIG(wheelevent)
+/*! \reimp */
+void QQuickWindow::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QQuickWindow);
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
+ event->angleDelta().x(), event->angleDelta().y());
+
+ qCDebug(lcMouse) << event;
+
+ //if the actual wheel event was accepted, accept the compatibility wheel event and return early
+ if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
+ return;
+
+ event->ignore();
+ d->deliverSinglePointEventUntilAccepted(event);
+ d->lastWheelEventAccepted = event->isAccepted();
+}
+#endif // wheelevent
+
+#if QT_CONFIG(tabletevent)
+/*! \reimp */
+void QQuickWindow::tabletEvent(QTabletEvent *event)
+{
+ Q_D(QQuickWindow);
+ qCDebug(lcTablet) << event;
+ // TODO Qt 6: make sure TabletEnterProximity and TabletLeaveProximity are delivered here
+ d->deliverPointerEvent(event);
+}
+#endif // tabletevent
+
+bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
+{
+ qCDebug(lcTouch) << event;
+
+ // An incoming TouchCancel event will typically not contain any points,
+ // but sendTouchCancelEvent() adds the points that have grabbers to the event.
+ // Deliver it to all items and handlers that have active touches.
+ const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(event->pointingDevice()))->
+ sendTouchCancelEvent(event);
+ cancelTouchMouseSynthesis();
+ return true;
+}
+
+void QQuickWindowPrivate::deliverDelayedTouchEvent()
+{
+ // Deliver and delete delayedTouch.
+ // Set delayedTouch to nullptr before delivery to avoid redelivery in case of
+ // event loop recursions (e.g if it the touch starts a dnd session).
+ QScopedPointer<QTouchEvent> e(delayedTouch.take());
+ qCDebug(lcTouchCmprs) << "delivering" << e.data();
+ deliverPointerEvent(e.data());
+}
+
+/*! \internal
+ The handler for the QEvent::WindowDeactivate event, and also when
+ Qt::ApplicationState tells us the application is no longer active.
+ It clears all exclusive grabs of items and handlers whose window is this one,
+ for all known pointing devices.
+
+ The QEvent is not passed into this function because in the first case it's
+ just a plain QEvent with no extra data, and because the application state
+ change is delivered via a signal rather than an event.
+*/
+void QQuickWindowPrivate::handleWindowDeactivate()
+{
+ Q_Q(QQuickWindow);
+ qCDebug(lcFocus) << "deactivated" << windowTitle;
+ const auto inputDevices = QInputDevice::devices();
+ for (auto device : inputDevices) {
+ if (auto pointingDevice = qobject_cast<const QPointingDevice *>(device)) {
+ auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(pointingDevice));
+ for (auto epd : devPriv->activePoints.values()) {
+ if (!epd.exclusiveGrabber.isNull()) {
+ bool relevant = false;
+ if (QQuickItem *item = qmlobject_cast<QQuickItem *>(epd.exclusiveGrabber.data()))
+ relevant = (item->window() == q);
+ else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(epd.exclusiveGrabber.data()))
+ relevant = (handler->parentItem()->window() == q);
+ if (relevant)
+ devPriv->setExclusiveGrabber(nullptr, epd.eventPoint, nullptr);
+ }
+ // For now, we don't clearPassiveGrabbers(), just in case passive grabs
+ // can be useful to keep monitoring the mouse even after window deactivation.
+ }
+ }
+ }
+}
+
+bool QQuickWindowPrivate::allUpdatedPointsAccepted(const QPointerEvent *ev)
+{
+ for (auto &point : ev->points()) {
+ if (point.state() != QEventPoint::State::Pressed && !point.isAccepted())
+ return false;
+ }
+ return true;
+}
+
+/*! \internal
+ Localize \a ev for delivery to \a dest.
+
+ Unlike QMutableTouchEvent::localized(), this modifies the QEventPoint
+ instances in \a ev, which is more efficient than making a copy.
+*/
+void QQuickWindowPrivate::localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest)
+{
+ for (int i = 0; i < ev->pointCount(); ++i) {
+ auto &point = QMutableEventPoint::from(ev->point(i));
+ QMutableEventPoint::from(point).setPosition(dest->mapFromScene(point.scenePosition()));
+ }
+}
+
+QList<QObject *> QQuickWindowPrivate::exclusiveGrabbers(QPointerEvent *ev)
+{
+ QList<QObject *> result;
+ for (const QEventPoint &point : ev->points()) {
+ if (QObject *grabber = ev->exclusiveGrabber(point)) {
+ if (!result.contains(grabber))
+ result << grabber;
+ }
+ }
+ return result;
+}
+
+bool QQuickWindowPrivate::isMouseEvent(const QPointerEvent *ev)
+{
+ switch (ev->type()) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QQuickWindowPrivate::isTouchEvent(const QPointerEvent *ev)
+{
+ switch (ev->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QQuickWindowPrivate::isTabletEvent(const QPointerEvent *ev)
+{
+ switch (ev->type()) {
+ case QEvent::TabletPress:
+ case QEvent::TabletMove:
+ case QEvent::TabletRelease:
+ case QEvent::TabletEnterProximity:
+ case QEvent::TabletLeaveProximity:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
+{
+ Q_Q(QQuickWindow);
+ QEventPoint::States states = event->touchPointStates();
+ if (states.testFlag(QEventPoint::State::Pressed) || states.testFlag(QEventPoint::State::Released)) {
+ // we can only compress an event that doesn't include any pressed or released points
+ return false;
+ }
+
+ if (!delayedTouch) {
+ delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), event->points()));
+ delayedTouch->setTimestamp(event->timestamp());
+ qCDebug(lcTouchCmprs) << "delayed" << delayedTouch.data();
+ if (renderControl)
+ QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
+ else if (windowManager)
+ windowManager->maybeUpdate(q);
+ return true;
+ }
+
+ // check if this looks like the last touch event
+ if (delayedTouch->type() == event->type() &&
+ delayedTouch->device() == event->device() &&
+ delayedTouch->modifiers() == event->modifiers() &&
+ delayedTouch->pointCount() == event->pointCount())
+ {
+ // possible match.. is it really the same?
+ bool mismatch = false;
+
+ auto tpts = event->points();
+ for (qsizetype i = 0; i < event->pointCount(); ++i) {
+ const auto &tp = tpts.at(i);
+ const auto &tpDelayed = delayedTouch->point(i);
+ if (tp.id() != tpDelayed.id()) {
+ mismatch = true;
+ break;
+ }
+
+ if (tpDelayed.state() == QEventPoint::State::Updated && tp.state() == QEventPoint::State::Stationary)
+ QMutableEventPoint::from(tpts[i]).setState(QEventPoint::State::Updated);
+ }
+
+ // matching touch event? then give delayedTouch a merged set of touchpoints
+ if (!mismatch) {
+ // have to create a new event because QMutableTouchEvent::setTouchPoints() is missing
+ // TODO optimize, or move event compression elsewhere
+ delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), tpts));
+ delayedTouch->setTimestamp(event->timestamp());
+ qCDebug(lcTouchCmprs) << "coalesced" << delayedTouch.data();
+ return true;
+ }
+ }
+
+ // merging wasn't possible, so deliver the delayed event first, and then delay this one
+ deliverDelayedTouchEvent();
+ delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(),
+ event->modifiers(), event->points()));
+ delayedTouch->setTimestamp(event->timestamp());
+ return true;
+}
+
+// entry point for touch event delivery:
+// - translate the event to window coordinates
+// - compress the event instead of delivering it if applicable
+// - call deliverTouchPoints to actually dispatch the points
+void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
+{
+ translateTouchEvent(event);
+ // TODO remove: touch and mouse should be independent until we come to touch->mouse synth
+ if (event->pointCount()) {
+ auto &point = event->point(0);
+ if (point.state() == QEventPoint::State::Released) {
+ lastMousePosition = QPointF();
+ } else {
+ lastMousePosition = point.position();
+ }
+ }
+
+ qCDebug(lcTouch) << event;
+
+ static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
+
+ if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
+ deliverPointerEvent(event);
+ return;
+ }
+
+ if (!compressTouchEvent(event)) {
+ if (delayedTouch) {
+ deliverDelayedTouchEvent();
+ qCDebug(lcTouchCmprs) << "resuming delivery" << event;
+ }
+ deliverPointerEvent(event);
+ }
+}
+
+/*! \reimp */
+void QQuickWindow::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+/*! \reimp */
+void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QQuickWindow);
+ d->handleMouseEvent(event);
+}
+
+void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
+{
+ if (event->source() == Qt::MouseEventSynthesizedBySystem) {
+ event->accept();
+ return;
+ }
+ qCDebug(lcMouse) << event;
+
+ switch (event->type()) {
+ case QEvent::MouseButtonPress:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
+ event->buttons());
+ deliverPointerEvent(event);
+ break;
+ case QEvent::MouseButtonRelease:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
+ event->buttons());
+ deliverPointerEvent(event);
+#if QT_CONFIG(cursor)
+ updateCursor(event->scenePosition());
+#endif
+ break;
+ case QEvent::MouseButtonDblClick:
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
+ event->button(), event->buttons());
+ if (allowDoubleClick)
+ deliverPointerEvent(event);
+ break;
+ case QEvent::MouseMove: {
+ Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
+ event->position().x(), event->position().y());
+
+ qCDebug(lcHoverTrace) << this;
+
+ #if QT_CONFIG(cursor)
+ updateCursor(event->scenePosition());
+ #endif
+ if (!event->points().count() || !event->exclusiveGrabber(event->point(0))) {
+ QPointF last = lastMousePosition.isNull() ? event->scenePosition() : lastMousePosition;
+ lastMousePosition = event->scenePosition();
+
+ bool accepted = event->isAccepted();
+ bool delivered = deliverHoverEvent(contentItem, event->scenePosition(), last, event->modifiers(), event->timestamp(), accepted);
+ if (!delivered) {
+ //take care of any exits
+ accepted = clearHover(event->timestamp());
+ }
+ event->setAccepted(accepted);
+ }
+ deliverPointerEvent(event);
+ break;
+ }
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+}
+
+void QQuickWindowPrivate::flushFrameSynchronousEvents()
+{
+ Q_Q(QQuickWindow);
+
+ if (delayedTouch) {
+ deliverDelayedTouchEvent();
+
+ // Touch events which constantly start animations (such as a behavior tracking
+ // the mouse point) need animations to start.
+ QQmlAnimationTimer *ut = QQmlAnimationTimer::instance();
+ if (ut && ut->hasStartAnimationPending())
+ ut->startAnimations();
+ }
+
+ // In webOS we already have the alternative to the issue that this
+ // wanted to address and thus skipping this part won't break anything.
+#if !defined(Q_OS_WEBOS)
+ // Once per frame, if any items are dirty, send a synthetic hover,
+ // in case items have changed position, visibility, etc.
+ // For instance, during animation (including the case of a ListView
+ // whose delegates contain MouseAreas), a MouseArea needs to know
+ // whether it has moved into a position where it is now under the cursor.
+ // TODO do this for each known mouse device or come up with a different strategy
+ if (!q->mouseGrabberItem() && !lastMousePosition.isNull() && dirtyItemList) {
+ bool accepted = false;
+ bool delivered = deliverHoverEvent(contentItem, lastMousePosition, lastMousePosition, QGuiApplication::keyboardModifiers(), 0, accepted);
+ if (!delivered)
+ clearHover(); // take care of any exits
+ }
+#endif
+}
+
+void QQuickWindowPrivate::onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition,
+ const QPointerEvent *event, const QEventPoint &point)
+{
+ qCDebug(lcPtrGrab) << grabber << transition << event << point;
+ // note: event can be null, if the signal was emitted from QPointingDevicePrivate::removeGrabber(grabber)
+ if (auto *handler = qmlobject_cast<QQuickPointerHandler *>(grabber)) {
+ handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
+ const_cast<QEventPoint &>(point));
+ } else {
+ switch (transition) {
+ case QPointingDevice::CancelGrabExclusive:
+ case QPointingDevice::UngrabExclusive:
+ if (auto *item = qmlobject_cast<QQuickItem *>(grabber)) {
+ bool filtered = false;
+ if (isDeliveringTouchAsMouse() ||
+ point.device()->type() == QInputDevice::DeviceType::Mouse ||
+ point.device()->type() == QInputDevice::DeviceType::TouchPad) {
+ QMutableSinglePointEvent e(QEvent::UngrabMouse, point.device(), point);
+ hasFiltered.clear();
+ filtered = sendFilteredMouseEvent(&e, item, item->parentItem());
+ if (!filtered) {
+ lastUngrabbed = item;
+ item->mouseUngrabEvent();
+ }
+ }
+ if (point.device()->type() == QInputDevice::DeviceType::TouchScreen) {
+ bool allReleasedOrCancelled = true;
+ if (transition == QPointingDevice::UngrabExclusive && event) {
+ for (const auto &pt : event->points()) {
+ if (pt.state() != QEventPoint::State::Released) {
+ allReleasedOrCancelled = false;
+ break;
+ }
+ }
+ }
+ if (allReleasedOrCancelled)
+ item->touchUngrabEvent();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void QQuickWindowPrivate::ensureDeviceConnected(const QPointingDevice *dev)
+{
+ if (knownPointingDevices.contains(dev))
+ return;
+ knownPointingDevices.append(dev);
+ connect(dev, &QPointingDevice::grabChanged, this, &QQuickWindowPrivate::onGrabChanged);
+}
+
+void QQuickWindowPrivate::deliverPointerEvent(QPointerEvent *event)
+{
+ // If users spin the eventloop as a result of event delivery, we disable
+ // event compression and send events directly. This is because we consider
+ // the usecase a bit evil, but we at least don't want to lose events.
+ ++pointerEventRecursionGuard;
+ eventsInDelivery.push(event);
+
+ skipDelivery.clear();
+ QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->pointingDevice()).clear();
+ qCDebug(lcPtr) << "delivering" << event;
+ for (int i = 0; i < event->pointCount(); ++i)
+ event->point(i).setAccepted(false);
+
+ if (event->isBeginEvent()) {
+ ensureDeviceConnected(event->pointingDevice());
+ if (!deliverPressOrReleaseEvent(event))
+ event->setAccepted(false);
+ }
+ if (!allUpdatedPointsAccepted(event))
+ deliverUpdatedPoints(event);
+ if (event->isEndEvent())
+ deliverPressOrReleaseEvent(event, true);
+
+ // failsafe: never allow any kind of grab to persist after release
+ if (event->isEndEvent()) {
+ if (isTouchEvent(event)) {
+ for (int i = 0; i < event->pointCount(); ++i) {
+ auto &point = event->point(i);
+ if (point.state() == QEventPoint::State::Released) {
+ event->setExclusiveGrabber(point, nullptr);
+ event->clearPassiveGrabbers(point);
+ }
+ }
+ // never allow touch->mouse synthesis to persist either
+ cancelTouchMouseSynthesis();
+ } else if (static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) {
+ auto &firstPt = event->point(0);
+ event->setExclusiveGrabber(firstPt, nullptr);
+ event->clearPassiveGrabbers(firstPt);
+ }
+ }
+
+ eventsInDelivery.pop();
+ --pointerEventRecursionGuard;
+ lastUngrabbed = nullptr;
+}
+
+// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
+// FIXME: should this be iterative instead of recursive?
+// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
+// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
+// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointerEvent *event, const QEventPoint &point,
+ bool checkMouseButtons, bool checkAcceptsTouch) const
+{
+ QVector<QQuickItem *> targets;
+ auto itemPrivate = QQuickItemPrivate::get(item);
+ QPointF itemPos = item->mapFromScene(point.scenePosition());
+ bool relevant = item->contains(itemPos);
+ // if the item clips, we can potentially return early
+ if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
+ if (!relevant)
+ return targets;
+ }
+
+ if (itemPrivate->hasPointerHandlers()) {
+ if (!relevant)
+ if (itemPrivate->anyPointerHandlerWants(event, point))
+ relevant = true;
+ } else {
+ if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
+ relevant = false;
+ if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
+ relevant = false;
+ }
+
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ if (relevant) {
+ auto it = std::lower_bound(children.begin(), children.end(), 0,
+ [](auto lhs, auto rhs) -> bool { return lhs->z() < rhs; });
+ children.insert(it, item);
+ }
+
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QQuickItem *child = children.at(ii);
+ auto childPrivate = QQuickItemPrivate::get(child);
+ if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
+ continue;
+
+ if (child != item)
+ targets << pointerTargets(child, event, point, checkMouseButtons, checkAcceptsTouch);
+ else
+ targets << child;
+ }
+
+ return targets;
+}
+
+// return the joined lists
+// list1 has priority, common items come last
+QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
+{
+ QVector<QQuickItem *> targets = list1;
+ // start at the end of list2
+ // if item not in list, append it
+ // if item found, move to next one, inserting before the last found one
+ int insertPosition = targets.length();
+ for (int i = list2.length() - 1; i >= 0; --i) {
+ int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition);
+ if (newInsertPosition >= 0) {
+ Q_ASSERT(newInsertPosition <= insertPosition);
+ insertPosition = newInsertPosition;
+ }
+ // check for duplicates, only insert if the item isn't there already
+ if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition))
+ targets.insert(insertPosition, list2.at(i));
+ }
+ return targets;
+}
+
+/*! \internal
+ Deliver updated points to existing grabbers.
+*/
+void QQuickWindowPrivate::deliverUpdatedPoints(QPointerEvent *event)
+{
+ bool done = false;
+ const auto grabbers = exclusiveGrabbers(event);
+ hasFiltered.clear();
+ for (auto grabber : grabbers) {
+ // The grabber is guaranteed to be either an item or a handler.
+ QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber);
+ if (!receiver) {
+ // The grabber is not an item? It's a handler then. Let it have the event first.
+ QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
+ receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
+ hasFiltered.clear();
+ if (sendFilteredPointerEvent(event, receiver))
+ done = true;
+ localizePointerEvent(event, receiver);
+ handler->handlePointerEvent(event);
+ }
+ if (done)
+ break;
+ // If the grabber is an item or the grabbing handler didn't handle it,
+ // then deliver the event to the item (which may have multiple handlers).
+ hasFiltered.clear();
+ deliverMatchingPointsToItem(receiver, true, event);
+ }
+
+ // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
+ for (auto &point : event->points())
+ deliverToPassiveGrabbers(event->passiveGrabbers(point), event);
+
+ if (done)
+ return;
+
+ // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order
+ if (!event->allPointsGrabbed()) {
+ QVector<QQuickItem *> targetItems;
+ for (auto &point : event->points()) {
+ // Presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints.
+ // Don't find handlers for points that are already grabbed by an Item (such as Flickable).
+ if (point.state() == QEventPoint::Pressed || qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(point)))
+ continue;
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, event, point, false, false);
+ if (targetItems.count()) {
+ targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
+ } else {
+ targetItems = targetItemsForPoint;
+ }
+ }
+ for (QQuickItem *item : targetItems) {
+ if (grabbers.contains(item))
+ continue;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ localizePointerEvent(event, item);
+ itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers
+ if (event->allPointsGrabbed())
+ break;
+ }
+ }
+}
+
+// Deliver an event containing newly pressed or released touch points
+bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QPointerEvent *event, bool handlersOnly)
+{
+ QVector<QQuickItem *> targetItems;
+ const bool isTouch = isTouchEvent(event);
+ if (isTouch && event->isBeginEvent() && isDeliveringTouchAsMouse()) {
+ if (auto point = const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(touchMouseDevice))->queryPointById(touchMouseId)) {
+ // When a second point is pressed, if the first point's existing
+ // grabber was a pointer handler while a filtering parent is filtering
+ // the same first point _as mouse_: we're starting over with delivery,
+ // so we need to allow the second point to now be sent as a synth-mouse
+ // instead of the first one, so that filtering parents (maybe even the
+ // same one) can get a chance to see the second touchpoint as a
+ // synth-mouse and perhaps grab it. Ideally we would always do this
+ // when a new touchpoint is pressed, but this compromise fixes
+ // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable
+ if (qobject_cast<QQuickPointerHandler *>(event->exclusiveGrabber(point->eventPoint)))
+ cancelTouchMouseSynthesis();
+ } else {
+ qCWarning(lcTouchTarget) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event;
+ }
+ }
+ for (int i = 0; i < event->pointCount(); ++i) {
+ auto &point = event->point(i);
+ if (point.state() == QEventPoint::Pressed)
+ event->clearPassiveGrabbers(point);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, event, point, !isTouch, isTouch);
+ if (targetItems.count()) {
+ targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
+ } else {
+ targetItems = targetItemsForPoint;
+ }
+ }
+
+ for (QQuickItem *item : targetItems) {
+ hasFiltered.clear();
+ if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
+ if (event->isAccepted())
+ return true;
+ skipDelivery.append(item);
+ }
+
+ // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event,
+ // nor to any item which already had a chance to filter.
+ if (skipDelivery.contains(item))
+ continue;
+
+ // sendFilteredPointerEvent() changed the QEventPoint::accepted() state,
+ // but per-point acceptance is opt-in during normal delivery to items.
+ for (int i = 0; i < event->pointCount(); ++i)
+ event->point(i).setAccepted(false);
+
+ deliverMatchingPointsToItem(item, false, event, handlersOnly);
+ if (event->allPointsAccepted())
+ handlersOnly = true;
+ }
+
+ return event->allPointsAccepted();
+}
+
+void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly)
+{
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ // QTBUG-85379
+ // In QT_VERSION below 6.0.0 touchEnabled for QtQuickItems is set by default to true
+ // It causes delivering touch events to Items which are not interested
+ // In some cases (like using Material Style in Android) it may cause a crash
+ if (itemPrivate->wasDeleted)
+ return;
+#endif
+ localizePointerEvent(pointerEvent, item);
+ bool isMouse = isMouseEvent(pointerEvent);
+
+ // Let the Item's handlers (if any) have the event first.
+ // However, double click should never be delivered to handlers.
+ if (pointerEvent->type() != QEvent::MouseButtonDblClick) {
+ bool wasAccepted = pointerEvent->allPointsAccepted();
+ itemPrivate->handlePointerEvent(pointerEvent);
+ allowDoubleClick = wasAccepted || !(isMouse && pointerEvent->isBeginEvent() && pointerEvent->allPointsAccepted());
+ }
+ if (handlersOnly)
+ return;
+
+ // If all points are released and the item is not the grabber, it doesn't get the event.
+ // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
+ if (pointerEvent->isEndEvent() && !pointerEvent->isUpdateEvent()
+ && !exclusiveGrabbers(pointerEvent).contains(item))
+ return;
+
+ // If any parent filters the event, we're done.
+ if (sendFilteredPointerEvent(pointerEvent, item))
+ return;
+
+ // TODO: unite this mouse point delivery with the synthetic mouse event below
+ if (isMouse) {
+ auto button = static_cast<QSinglePointEvent *>(pointerEvent)->button();
+ if ((isGrabber && button == Qt::NoButton) || item->acceptedMouseButtons().testFlag(button)) {
+ // The only reason to already have a mouse grabber here is
+ // synthetic events - flickable sends one when setPressDelay is used.
+ auto oldMouseGrabber = pointerEvent->exclusiveGrabber(pointerEvent->point(0));
+ pointerEvent->accept();
+ if (isGrabber && sendFilteredPointerEvent(pointerEvent, item))
+ return;
+ localizePointerEvent(pointerEvent, item);
+ QCoreApplication::sendEvent(item, pointerEvent);
+ if (pointerEvent->isAccepted()) {
+ auto &point = pointerEvent->point(0);
+ auto mouseGrabber = pointerEvent->exclusiveGrabber(point);
+ if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
+ // Normally we don't need item->mouseUngrabEvent() here, because QQuickWindowPrivate::onGrabChanged does it.
+ // However, if one item accepted the mouse event, it expects to have the grab and be in "pressed" state,
+ // because accepting implies grabbing. But before it actually gets the grab, another item could steal it.
+ // In that case, onGrabChanged() does NOT notify the item that accepted the event that it's not getting the grab after all.
+ // So after ensuring that it's not redundant, we send a notification here, for that case (QTBUG-55325).
+ if (item != lastUngrabbed) {
+ item->mouseUngrabEvent();
+ lastUngrabbed = item;
+ }
+ } else if (item->isEnabled() && item->isVisible() && point.state() != QEventPoint::State::Released) {
+ pointerEvent->setExclusiveGrabber(point, item);
+ }
+ point.setAccepted(true);
+ }
+ return;
+ }
+ }
+
+ if (!isTouchEvent(pointerEvent))
+ return;
+
+ bool eventAccepted = false;
+ QMutableTouchEvent touchEvent;
+ QQuickItemPrivate::get(item)->localizedTouchEvent(static_cast<QTouchEvent *>(pointerEvent), false, &touchEvent);
+ if (touchEvent.type() == QEvent::None)
+ return; // no points inside this item
+
+ if (item->acceptTouchEvents()) {
+ qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item;
+
+ // If any parent filters the event, we're done.
+ hasFiltered.clear();
+ if (sendFilteredPointerEvent(&touchEvent, item))
+ return;
+
+ // Deliver the touch event to the given item
+ qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
+ QCoreApplication::sendEvent(item, &touchEvent);
+ eventAccepted = touchEvent.isAccepted();
+ } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
+ // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
+ if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
+ // send mouse event
+ if (deliverTouchAsMouse(item, &touchEvent))
+ eventAccepted = true;
+ }
+ }
+
+ if (eventAccepted) {
+ // If the touch was accepted (regardless by whom or in what form),
+ // update accepted new points.
+ bool isPressOrRelease = pointerEvent->isBeginEvent() || pointerEvent->isEndEvent();
+ for (int i = 0; i < touchEvent.pointCount(); ++i) {
+ auto &point = QMutableEventPoint::from(touchEvent.point(i));
+ // legacy-style delivery: if the item doesn't reject the event, that means it handled ALL the points
+ point.setAccepted();
+ if (isPressOrRelease)
+ pointerEvent->setExclusiveGrabber(point, item);
+ }
+ } else {
+ // But if the event was not accepted then we know this item
+ // will not be interested in further updates for those touchpoint IDs either.
+ for (const auto &point: touchEvent.points()) {
+ if (point.state() == QEventPoint::State::Pressed) {
+ if (pointerEvent->exclusiveGrabber(point) == item) {
+ qCDebug(lcTouchTarget) << "TP" << Qt::hex << point.id() << "disassociated";
+ pointerEvent->setExclusiveGrabber(point, nullptr);
+ }
+ }
+ }
+ }
+}
+
+#if QT_CONFIG(quick_draganddrop)
+void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
+{
+ grabber->resetTarget();
+ QQuickDragGrabber::iterator grabItem = grabber->begin();
+ if (grabItem != grabber->end()) {
+ Q_ASSERT(event->type() != QEvent::DragEnter);
+ if (event->type() == QEvent::Drop) {
+ QDropEvent *e = static_cast<QDropEvent *>(event);
+ for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
+ QPointF p = (**grabItem)->mapFromScene(e->position().toPoint());
+ QDropEvent translatedEvent(
+ p.toPoint(),
+ e->possibleActions(),
+ e->mimeData(),
+ e->buttons(),
+ e->modifiers());
+ QQuickDropEventEx::copyActions(&translatedEvent, *e);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
+ e->setAccepted(translatedEvent.isAccepted());
+ e->setDropAction(translatedEvent.dropAction());
+ grabber->setTarget(**grabItem);
+ }
+ }
+ if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
+ QDragLeaveEvent leaveEvent;
+ for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
+ QCoreApplication::sendEvent(**grabItem, &leaveEvent);
+ return;
+ } else {
+ QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
+
+ // Used to ensure we don't send DragEnterEvents to current drop targets,
+ // and to detect which current drop targets we have left
+ QVarLengthArray<QQuickItem*, 64> currentGrabItems;
+ for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
+ currentGrabItems.append(**grabItem);
+
+ // Look for any other potential drop targets that are higher than the current ones
+ QDragEnterEvent enterEvent(
+ moveEvent->position().toPoint(),
+ moveEvent->possibleActions(),
+ moveEvent->mimeData(),
+ moveEvent->buttons(),
+ moveEvent->modifiers());
+ QQuickDropEventEx::copyActions(&enterEvent, *moveEvent);
+ event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent, &currentGrabItems));
+
+ for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
+ int i = currentGrabItems.indexOf(**grabItem);
+ if (i >= 0) {
+ currentGrabItems.remove(i);
+ // Still grabbed: send move event
+ QDragMoveEvent translatedEvent(
+ (**grabItem)->mapFromScene(moveEvent->position().toPoint()).toPoint(),
+ moveEvent->possibleActions(),
+ moveEvent->mimeData(),
+ moveEvent->buttons(),
+ moveEvent->modifiers());
+ QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
+ QCoreApplication::sendEvent(**grabItem, &translatedEvent);
+ event->setAccepted(translatedEvent.isAccepted());
+ QQuickDropEventEx::copyActions(moveEvent, translatedEvent);
+ }
+ }
+
+ // Anything left in currentGrabItems is no longer a drop target and should be sent a DragLeaveEvent
+ QDragLeaveEvent leaveEvent;
+ for (QQuickItem *i : currentGrabItems)
+ QCoreApplication::sendEvent(i, &leaveEvent);
+
+ return;
+ }
+ }
+ if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
+ QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
+ QDragEnterEvent enterEvent(
+ e->position().toPoint(),
+ e->possibleActions(),
+ e->mimeData(),
+ e->buttons(),
+ e->modifiers());
+ QQuickDropEventEx::copyActions(&enterEvent, *e);
+ event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent));
+ }
+}
+
+bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems)
+{
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
+ return false;
+ QPointF p = item->mapFromScene(event->position().toPoint());
+ bool itemContained = item->contains(p);
+
+ if (!itemContained && itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
+ return false;
+ }
+
+ QDragEnterEvent enterEvent(
+ event->position().toPoint(),
+ event->possibleActions(),
+ event->mimeData(),
+ event->buttons(),
+ event->modifiers());
+ QQuickDropEventEx::copyActions(&enterEvent, *event);
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+
+ // Check children in front of this item first
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ if (children.at(ii)->z() < 0)
+ continue;
+ if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems))
+ return true;
+ }
+
+ if (itemContained) {
+ // If this item is currently grabbed, don't send it another DragEnter,
+ // just grab it again if it's still contained.
+ if (currentGrabItems && currentGrabItems->contains(item)) {
+ grabber->grab(item);
+ grabber->setTarget(item);
+ return true;
+ }
+
+ if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
+ QDragMoveEvent translatedEvent(
+ p.toPoint(),
+ event->possibleActions(),
+ event->mimeData(),
+ event->buttons(),
+ event->modifiers(),
+ event->type());
+ QQuickDropEventEx::copyActions(&translatedEvent, *event);
+ translatedEvent.setAccepted(event->isAccepted());
+ QCoreApplication::sendEvent(item, &translatedEvent);
+ event->setAccepted(translatedEvent.isAccepted());
+ event->setDropAction(translatedEvent.dropAction());
+ if (event->type() == QEvent::DragEnter) {
+ if (translatedEvent.isAccepted()) {
+ grabber->grab(item);
+ grabber->setTarget(item);
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ }
+
+ // Check children behind this item if this item or any higher children have not accepted
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ if (children.at(ii)->z() >= 0)
+ continue;
+ if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems))
+ return true;
+ }
+
+ return false;
+}
+#endif // quick_draganddrop
+
+#if QT_CONFIG(cursor)
+void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
+{
+ Q_Q(QQuickWindow);
+
+ auto cursorItemAndHandler = findCursorItemAndHandler(contentItem, scenePos);
+
+ if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) {
+ QWindow *renderWindow = QQuickRenderControl::renderWindowFor(q);
+ QWindow *window = renderWindow ? renderWindow : q;
+ cursorItem = cursorItemAndHandler.first;
+ cursorHandler = cursorItemAndHandler.second;
+ if (cursorItem)
+ window->setCursor(QQuickItemPrivate::get(cursorItem)->effectiveCursor(cursorHandler));
+ else
+ window->unsetCursor();
+ }
+}
+
+QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const
+{
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(scenePos);
+ if (!item->contains(p))
+ return {nullptr, nullptr};
+ }
+
+ if (itemPrivate->subtreeCursorEnabled) {
+ QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QQuickItem *child = children.at(ii);
+ if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
+ continue;
+ auto ret = findCursorItemAndHandler(child, scenePos);
+ if (ret.first)
+ return ret;
+ }
+ if (itemPrivate->hasCursorHandler) {
+ if (auto handler = itemPrivate->effectiveCursorHandler()) {
+ if (handler->parentContains(scenePos))
+ return {item, handler};
+ }
+ }
+ if (itemPrivate->hasCursor) {
+ QPointF p = item->mapFromScene(scenePos);
+ if (item->contains(p))
+ return {item, nullptr};
+ }
+ }
+
+ return {nullptr, nullptr};
+}
+#endif
+
+bool QQuickWindowPrivate::sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
+{
+ return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
+}
+
+bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
+{
+ if (!allowChildEventFiltering)
+ return false;
+ if (!filteringParent)
+ return false;
+ bool filtered = false;
+ const bool hasHandlers = QQuickItemPrivate::get(receiver)->hasPointerHandlers();
+ if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(filteringParent)) {
+ hasFiltered.append(filteringParent);
+ if (isMouseEvent(event)) {
+ if (receiver->acceptedMouseButtons()) {
+ const bool wasAccepted = event->allPointsAccepted();
+ Q_ASSERT(event->pointCount());
+ localizePointerEvent(event, receiver);
+ event->setAccepted(true);
+ auto oldMouseGrabber = event->exclusiveGrabber(event->point(0));
+ if (filteringParent->childMouseEventFilter(receiver, event)) {
+ qCDebug(lcMouse) << "mouse event intercepted by childMouseEventFilter of " << filteringParent;
+ skipDelivery.append(filteringParent);
+ filtered = true;
+ if (event->isAccepted() && event->isBeginEvent()) {
+ auto &point = event->point(0);
+ auto mouseGrabber = event->exclusiveGrabber(point);
+ if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) {
+ receiver->mouseUngrabEvent();
+ } else {
+ event->setExclusiveGrabber(point, receiver);
+ }
+ }
+ } else {
+ // Restore accepted state if the event was not filtered.
+ event->setAccepted(wasAccepted);
+ }
+ }
+ } else if (isTouchEvent(event)) {
+ const bool acceptsTouchEvents = receiver->acceptTouchEvents() || hasHandlers;
+ auto device = event->device();
+ if (device->type() == QInputDevice::DeviceType::TouchPad &&
+ device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) {
+ qCDebug(lcTouchTarget) << "skipping filtering of synth-mouse event from" << device;
+ } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) {
+ // get a touch event customized for delivery to filteringParent
+ // TODO should not be necessary? because QQuickWindowPrivate::deliverMatchingPointsToItem() does it
+ QMutableTouchEvent filteringParentTouchEvent;
+ QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true, &filteringParentTouchEvent);
+ if (filteringParentTouchEvent.type() != QEvent::None) {
+ qCDebug(lcTouch) << "letting parent" << filteringParent << "filter for" << receiver << &filteringParentTouchEvent;
+ if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) {
+ qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
+ skipDelivery.append(filteringParent);
+ for (auto point : filteringParentTouchEvent.points())
+ event->setExclusiveGrabber(point, filteringParent);
+ return true;
+ } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) &&
+ !filteringParent->acceptTouchEvents()) {
+ qCDebug(lcTouch) << "touch event NOT intercepted by childMouseEventFilter of " << filteringParent
+ << "; accepts touch?" << filteringParent->acceptTouchEvents()
+ << "receiver accepts touch?" << acceptsTouchEvents
+ << "so, letting parent filter a synth-mouse event";
+ // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
+ for (auto &tp : filteringParentTouchEvent.points()) {
+ QEvent::Type t;
+ switch (tp.state()) {
+ case QEventPoint::State::Pressed:
+ t = QEvent::MouseButtonPress;
+ break;
+ case QEventPoint::State::Released:
+ t = QEvent::MouseButtonRelease;
+ break;
+ case QEventPoint::State::Stationary:
+ continue;
+ default:
+ t = QEvent::MouseMove;
+ break;
+ }
+
+ bool touchMouseUnset = (touchMouseId == -1);
+ // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
+ if (touchMouseUnset || touchMouseId == tp.id()) {
+ // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.)
+ // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that
+ QMutableSinglePointEvent mouseEvent;
+ touchToMouseEvent(t, tp, &filteringParentTouchEvent, &mouseEvent);
+ // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
+ // report the touchpoint's grabber. Whenever we send a synthetic mouse event,
+ // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
+ touchMouseId = tp.id();
+ touchMouseDevice = event->pointingDevice();
+ if (filteringParent->childMouseEventFilter(receiver, &mouseEvent)) {
+ qCDebug(lcTouch) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
+ skipDelivery.append(filteringParent);
+ if (t != QEvent::MouseButtonRelease) {
+ qCDebug(lcTouchTarget) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent;
+ filteringParentTouchEvent.setExclusiveGrabber(tp, filteringParent);
+ touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
+ if (mouseEvent.isAccepted())
+ filteringParent->grabMouse();
+ }
+ filtered = true;
+ }
+ if (touchMouseUnset)
+ // Now that we're done sending a synth mouse event, and it wasn't grabbed,
+ // the touchpoint is no longer acting as a synthetic mouse. Restore previous state.
+ cancelTouchMouseSynthesis();
+ mouseEvent.point(0).setAccepted(false); // because touchToMouseEvent() set it true
+ // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter
+ // has been called once, we're done with this loop over the touchpoints.
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered;
+}
+
+bool QQuickWindowPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
+{
+ if (!filteringParent)
+ return false;
+
+ QQuickItemPrivate *filteringParentPrivate = QQuickItemPrivate::get(filteringParent);
+ if (filteringParentPrivate->replayingPressEvent)
+ return false;
+
+ bool filtered = false;
+ if (filteringParentPrivate->filtersChildMouseEvents && !hasFiltered.contains(filteringParent)) {
+ hasFiltered.append(filteringParent);
+ if (filteringParent->childMouseEventFilter(receiver, event)) {
+ filtered = true;
+ skipDelivery.append(filteringParent);
+ }
+ qCDebug(lcMouseTarget) << "for" << receiver << filteringParent << "childMouseEventFilter ->" << filtered;
+ }
+
+ return sendFilteredMouseEvent(event, receiver, filteringParent->parentItem()) || filtered;
+}
+
+bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold)
+{
+ QStyleHints *styleHints = QGuiApplication::styleHints();
+ bool dragVelocityLimitAvailable = event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
+ && styleHints->startDragVelocity();
+ bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
+ if (dragVelocityLimitAvailable) {
+ QVector2D velocityVec = event->point(0).velocity();
+ qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
+ overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
+ }
+ return overThreshold;
+}
+
+bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
+{
+ QStyleHints *styleHints = qApp->styleHints();
+ bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
+ const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
+ if (!overThreshold && dragVelocityLimitAvailable) {
+ qreal velocity = axis == Qt::XAxis ? tp.velocity().x() : tp.velocity().y();
+ overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
+ }
+ return overThreshold;
+}
+
+bool QQuickWindowPrivate::dragOverThreshold(QVector2D delta)
+{
+ int threshold = qApp->styleHints()->startDragDistance();
+ return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
+}
+
+/*!
+ \qmlproperty list<Object> Window::data
+ \default
+
+ The data property allows you to freely mix visual children, resources
+ and other Windows in a Window.
+
+ If you assign another Window to the data list, the nested window will
+ become "transient for" the outer Window.
+
+ If you assign an \l Item to the data list, it becomes a child of the
+ Window's \l contentItem, so that it appears inside the window. The item's
+ parent will be the window's contentItem, which is the root of the Item
+ ownership tree within that Window.
+
+ If you assign any other object type, it is added as a resource.
+
+ It should not generally be necessary to refer to the \c data property,
+ as it is the default property for Window and thus all child items are
+ automatically assigned to this property.
+
+ \sa QWindow::transientParent()
+ */
+
+void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObject *o)
+{
+ if (!o)
+ return;
+ QQuickWindow *that = static_cast<QQuickWindow *>(property->object);
+ if (QQuickWindow *window = qmlobject_cast<QQuickWindow *>(o)) {
+ qCDebug(lcTransient) << window << "is transient for" << that;
+ window->setTransientParent(that);
+ }
+ QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(that->contentItem())->data();
+ itemProperty.append(&itemProperty, o);
+}
+
+qsizetype QQuickWindowPrivate::data_count(QQmlListProperty<QObject> *property)
+{
+ QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
+ if (!win || !win->contentItem() || !QQuickItemPrivate::get(win->contentItem())->data().count)
+ return 0;
+ QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(win->contentItem())->data();
+ return itemProperty.count(&itemProperty);
+}
+
+QObject *QQuickWindowPrivate::data_at(QQmlListProperty<QObject> *property, qsizetype i)
+{
+ QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
+ QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(win->contentItem())->data();
+ return itemProperty.at(&itemProperty, i);
+}
+
+void QQuickWindowPrivate::data_clear(QQmlListProperty<QObject> *property)
+{
+ QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
+ QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(win->contentItem())->data();
+ itemProperty.clear(&itemProperty);
+}
+
+void QQuickWindowPrivate::data_replace(QQmlListProperty<QObject> *property, qsizetype i, QObject *o)
+{
+ QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
+ QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(win->contentItem())->data();
+ itemProperty.replace(&itemProperty, i, o);
+}
+
+void QQuickWindowPrivate::data_removeLast(QQmlListProperty<QObject> *property)
+{
+ QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
+ QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(win->contentItem())->data();
+ itemProperty.removeLast(&itemProperty);
+}
+
+bool QQuickWindowPrivate::isRenderable() const
+{
+ Q_Q(const QQuickWindow);
+ return ((q->isExposed() && q->isVisible())) && q->geometry().isValid();
+}
+
+void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName,
+ QString *translatedMessage,
+ QString *untranslatedMessage)
+{
+ const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow",
+ "Failed to initialize graphics backend for %1.");
+ *translatedMessage = QQuickWindow::tr(msg).arg(backendName);
+ *untranslatedMessage = QString::fromLatin1(msg).arg(backendName);
+}
+
+void QQuickWindowPrivate::cleanupNodes()
+{
+ for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
+ delete cleanupNodeList.at(ii);
+ cleanupNodeList.clear();
+}
+
+void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
+{
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (p->itemNodeInstance) {
+ delete p->itemNodeInstance;
+ p->itemNodeInstance = nullptr;
+
+ if (p->extra.isAllocated()) {
+ p->extra->opacityNode = nullptr;
+ p->extra->clipNode = nullptr;
+ p->extra->rootNode = nullptr;
+ }
+
+ p->paintNode = nullptr;
+
+ p->dirty(QQuickItemPrivate::Window);
+ }
+
+ // Qt 6: Make invalidateSceneGraph a virtual member of QQuickItem
+ if (p->flags & QQuickItem::ItemHasContents) {
+ const QMetaObject *mo = item->metaObject();
+ int index = mo->indexOfSlot("invalidateSceneGraph()");
+ if (index >= 0) {
+ const QMetaMethod &method = mo->method(index);
+ // Skip functions named invalidateSceneGraph() in QML items.
+ if (strstr(method.enclosingMetaObject()->className(), "_QML_") == nullptr)
+ method.invoke(item, Qt::DirectConnection);
+ }
+ }
+
+ for (int ii = 0; ii < p->childItems.count(); ++ii)
+ cleanupNodesOnShutdown(p->childItems.at(ii));
+}
+
+// This must be called from the render thread, with the main thread frozen
+void QQuickWindowPrivate::cleanupNodesOnShutdown()
+{
+ Q_Q(QQuickWindow);
+ cleanupNodes();
+ cleanupNodesOnShutdown(contentItem);
+ for (QSet<QQuickItem *>::const_iterator it = parentlessItems.begin(), cend = parentlessItems.end(); it != cend; ++it)
+ cleanupNodesOnShutdown(*it);
+ animationController->windowNodesDestroyed();
+ q->cleanupSceneGraph();
+}
+
+void QQuickWindowPrivate::updateDirtyNodes()
+{
+ qCDebug(lcDirty) << "QQuickWindowPrivate::updateDirtyNodes():";
+
+ cleanupNodes();
+
+ QQuickItem *updateList = dirtyItemList;
+ dirtyItemList = nullptr;
+ if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
+
+ while (updateList) {
+ QQuickItem *item = updateList;
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
+ itemPriv->removeFromDirtyList();
+
+ qCDebug(lcDirty) << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
+ updateDirtyNode(item);
+ }
+}
+
+static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d)
+{
+ const QList<QQuickItem *> childItems = d->paintOrderChildItems();
+ QQuickItem *before = nullptr;
+ for (int i=0; i<childItems.size(); ++i) {
+ QQuickItemPrivate *dd = QQuickItemPrivate::get(childItems.at(i));
+ // Perform the same check as the in fetchNextNode below.
+ if (dd->z() < 0 && (dd->explicitVisible || (dd->extra.isAllocated() && dd->extra->effectRefCount)))
+ before = childItems.at(i);
+ else
+ break;
+ }
+ return Q_UNLIKELY(before) ? QQuickItemPrivate::get(before)->itemNode() : nullptr;
+}
+
+static QSGNode *fetchNextNode(QQuickItemPrivate *itemPriv, int &ii, bool &returnedPaintNode)
+{
+ QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
+
+ for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
+ if (!childPrivate->explicitVisible &&
+ (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
+ continue;
+
+ ii++;
+ return childPrivate->itemNode();
+ }
+
+ if (itemPriv->paintNode && !returnedPaintNode) {
+ returnedPaintNode = true;
+ return itemPriv->paintNode;
+ }
+
+ for (; ii < orderedChildren.count(); ++ii) {
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
+ if (!childPrivate->explicitVisible &&
+ (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
+ continue;
+
+ ii++;
+ return childPrivate->itemNode();
+ }
+
+ return nullptr;
+}
+
+void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
+{
+ QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
+ quint32 dirty = itemPriv->dirtyAttributes;
+ itemPriv->dirtyAttributes = 0;
+
+ if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
+ (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
+ (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
+
+ QMatrix4x4 matrix;
+
+ if (itemPriv->x != 0. || itemPriv->y != 0.)
+ matrix.translate(itemPriv->x, itemPriv->y);
+
+ for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
+ itemPriv->transforms.at(ii)->applyTo(&matrix);
+
+ if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
+ QPointF origin = item->transformOriginPoint();
+ matrix.translate(origin.x(), origin.y());
+ if (itemPriv->scale() != 1.)
+ matrix.scale(itemPriv->scale(), itemPriv->scale());
+ if (itemPriv->rotation() != 0.)
+ matrix.rotate(itemPriv->rotation(), 0, 0, 1);
+ matrix.translate(-origin.x(), -origin.y());
+ }
+
+ itemPriv->itemNode()->setMatrix(matrix);
+ }
+
+ bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
+ ((item->clip() == false) != (itemPriv->clipNode() == nullptr));
+ int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
+ bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
+ ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr));
+
+ if (clipEffectivelyChanged) {
+ QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
+ (QSGNode *) itemPriv->itemNode();
+ QSGNode *child = itemPriv->rootNode();
+
+ if (item->clip()) {
+ Q_ASSERT(itemPriv->clipNode() == nullptr);
+ QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect());
+ itemPriv->extra.value().clipNode = clip;
+ clip->update();
+
+ if (!child) {
+ parent->reparentChildNodesTo(clip);
+ parent->appendChildNode(clip);
+ } else {
+ parent->removeChildNode(child);
+ clip->appendChildNode(child);
+ parent->appendChildNode(clip);
+ }
+
+ } else {
+ QQuickDefaultClipNode *clip = itemPriv->clipNode();
+ Q_ASSERT(clip);
+ parent->removeChildNode(clip);
+ if (child) {
+ clip->removeChildNode(child);
+ parent->appendChildNode(child);
+ } else {
+ clip->reparentChildNodesTo(parent);
+ }
+
+ delete itemPriv->clipNode();
+ itemPriv->extra->clipNode = nullptr;
+ }
+ }
+
+ if (effectRefEffectivelyChanged) {
+ if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
+ itemPriv->childContainerNode()->removeAllChildNodes();
+
+ QSGNode *parent = itemPriv->clipNode();
+ if (!parent)
+ parent = itemPriv->opacityNode();
+ if (!parent)
+ parent = itemPriv->itemNode();
+
+ if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
+ Q_ASSERT(itemPriv->rootNode() == nullptr);
+ QSGRootNode *root = new QSGRootNode();
+ itemPriv->extra->rootNode = root;
+ parent->reparentChildNodesTo(root);
+ parent->appendChildNode(root);
+ } else {
+ Q_ASSERT(itemPriv->rootNode() != nullptr);
+ QSGRootNode *root = itemPriv->rootNode();
+ parent->removeChildNode(root);
+ root->reparentChildNodesTo(parent);
+ delete itemPriv->rootNode();
+ itemPriv->extra->rootNode = nullptr;
+ }
+ }
+
+ if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
+ int ii = 0;
+ bool fetchedPaintNode = false;
+ QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
+ int desiredNodesSize = orderedChildren.size() + (itemPriv->paintNode ? 1 : 0);
+
+ // now start making current state match the promised land of
+ // desiredNodes. in the case of our current state matching desiredNodes
+ // (though why would we get ChildrenUpdateMask with no changes?) then we
+ // should make no changes at all.
+
+ // how many nodes did we process, when examining changes
+ int desiredNodesProcessed = 0;
+
+ // currentNode is how far, in our present tree, we have processed. we
+ // make use of this later on to trim the current child list if the
+ // desired list is shorter.
+ QSGNode *groupNode = itemPriv->childContainerNode();
+ QSGNode *currentNode = groupNode->firstChild();
+ int added = 0;
+ int removed = 0;
+ int replaced = 0;
+ QSGNode *desiredNode = nullptr;
+
+ while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, fetchedPaintNode))) {
+ // uh oh... reality and our utopic paradise are diverging!
+ // we need to reconcile this...
+ if (currentNode != desiredNode) {
+ // for now, we're just removing the node from the children -
+ // and replacing it with the new node.
+ if (desiredNode->parent())
+ desiredNode->parent()->removeChildNode(desiredNode);
+ groupNode->insertChildNodeAfter(desiredNode, currentNode);
+ groupNode->removeChildNode(currentNode);
+ replaced++;
+
+ // since we just replaced currentNode, we also need to reset
+ // the pointer.
+ currentNode = desiredNode;
+ }
+
+ currentNode = currentNode->nextSibling();
+ desiredNodesProcessed++;
+ }
+
+ // if we didn't process as many nodes as in the new list, then we have
+ // more nodes at the end of desiredNodes to append to our list.
+ // this will be the case when adding new nodes, for instance.
+ if (desiredNodesProcessed < desiredNodesSize) {
+ while ((desiredNode = fetchNextNode(itemPriv, ii, fetchedPaintNode))) {
+ if (desiredNode->parent())
+ desiredNode->parent()->removeChildNode(desiredNode);
+ groupNode->appendChildNode(desiredNode);
+ added++;
+ }
+ } else if (currentNode) {
+ // on the other hand, if we processed less than our current node
+ // tree, then nodes have been _removed_ from the scene, and we need
+ // to take care of that here.
+ while (currentNode) {
+ QSGNode *node = currentNode->nextSibling();
+ groupNode->removeChildNode(currentNode);
+ currentNode = node;
+ removed++;
+ }
+ }
+ }
+
+ if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
+ itemPriv->clipNode()->setRect(item->clipRect());
+ itemPriv->clipNode()->update();
+ }
+
+ if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
+ | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
+ {
+ qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
+ ? itemPriv->opacity() : qreal(0);
+
+ if (opacity != 1 && !itemPriv->opacityNode()) {
+ QSGOpacityNode *node = new QSGOpacityNode;
+ itemPriv->extra.value().opacityNode = node;
+
+ QSGNode *parent = itemPriv->itemNode();
+ QSGNode *child = itemPriv->clipNode();
+ if (!child)
+ child = itemPriv->rootNode();
+
+ if (child) {
+ parent->removeChildNode(child);
+ node->appendChildNode(child);
+ parent->appendChildNode(node);
+ } else {
+ parent->reparentChildNodesTo(node);
+ parent->appendChildNode(node);
+ }
+ }
+ if (itemPriv->opacityNode())
+ itemPriv->opacityNode()->setOpacity(opacity);
+ }
+
+ if (dirty & QQuickItemPrivate::ContentUpdateMask) {
+
+ if (itemPriv->flags & QQuickItem::ItemHasContents) {
+ updatePaintNodeData.transformNode = itemPriv->itemNode();
+ itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
+
+ Q_ASSERT(itemPriv->paintNode == nullptr ||
+ itemPriv->paintNode->parent() == nullptr ||
+ itemPriv->paintNode->parent() == itemPriv->childContainerNode());
+
+ if (itemPriv->paintNode && itemPriv->paintNode->parent() == nullptr) {
+ QSGNode *before = qquickitem_before_paintNode(itemPriv);
+ if (before && before->parent()) {
+ Q_ASSERT(before->parent() == itemPriv->childContainerNode());
+ itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, before);
+ } else {
+ itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
+ }
+ }
+ } else if (itemPriv->paintNode) {
+ delete itemPriv->paintNode;
+ itemPriv->paintNode = nullptr;
+ }
+ }
+
+#ifndef QT_NO_DEBUG
+ // Check consistency.
+
+ QList<QSGNode *> nodes;
+ nodes << itemPriv->itemNodeInstance
+ << itemPriv->opacityNode()
+ << itemPriv->clipNode()
+ << itemPriv->rootNode()
+ << itemPriv->paintNode;
+ nodes.removeAll(nullptr);
+
+ Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance);
+ for (int i=1; i<nodes.size(); ++i) {
+ QSGNode *n = nodes.at(i);
+ // Failing this means we messed up reparenting
+ Q_ASSERT(n->parent() == nodes.at(i-1));
+ // Only the paintNode and the one who is childContainer may have more than one child.
+ Q_ASSERT(n == itemPriv->paintNode || n == itemPriv->childContainerNode() || n->childCount() == 1);
+ }
+#endif
+
+}
+
+bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg)
+{
+ Q_Q(QQuickWindow);
+ static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWindow::sceneGraphError);
+ if (q->isSignalConnected(errorSignal)) {
+ emit q->sceneGraphError(error, msg);
+ return true;
+ }
+ return false;
+}
+
+void QQuickWindow::maybeUpdate()
+{
+ Q_D(QQuickWindow);
+ if (d->renderControl)
+ QQuickRenderControlPrivate::get(d->renderControl)->maybeUpdate();
+ else if (d->windowManager)
+ d->windowManager->maybeUpdate(this);
+}
+
+void QQuickWindow::cleanupSceneGraph()
+{
+ Q_D(QQuickWindow);
+ if (!d->renderer)
+ return;
+
+ delete d->renderer->rootNode();
+ delete d->renderer;
+ d->renderer = nullptr;
+
+ d->runAndClearJobs(&d->beforeSynchronizingJobs);
+ d->runAndClearJobs(&d->afterSynchronizingJobs);
+ d->runAndClearJobs(&d->beforeRenderingJobs);
+ d->runAndClearJobs(&d->afterRenderingJobs);
+ d->runAndClearJobs(&d->afterSwapJobs);
+}
+
+void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
+{
+ qCDebug(lcTransient) << this << "is transient for" << window;
+ setTransientParent(window);
+ disconnect(sender(), SIGNAL(windowChanged(QQuickWindow*)),
+ this, SLOT(setTransientParent_helper(QQuickWindow*)));
+}
+
+QOpenGLContext *QQuickWindowPrivate::openglContext()
+{
+#if QT_CONFIG(opengl)
+ if (context && context->isValid()) {
+ QSGRendererInterface *rif = context->sceneGraphContext()->rendererInterface(context);
+ if (rif) {
+ Q_Q(QQuickWindow);
+ return reinterpret_cast<QOpenGLContext *>(rif->getResource(q, QSGRendererInterface::OpenGLContextResource));
+ }
+ }
+#endif
+ return nullptr;
+}
+
+/*!
+ Returns true if the scene graph has been initialized; otherwise returns false.
+ */
+bool QQuickWindow::isSceneGraphInitialized() const
+{
+ Q_D(const QQuickWindow);
+ return d->context != nullptr && d->context->isValid();
+}
+
+/*!
+ \fn void QQuickWindow::frameSwapped()
+
+ This signal is emitted when a frame has been queued for presenting. With
+ vertical synchronization enabled the signal is emitted at most once per
+ vsync interval in a continuously animating scene.
+
+ This signal will be emitted from the scene graph rendering thread.
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::frameSwapped()
+
+ This signal is emitted when a frame has been queued for presenting. With
+ vertical synchronization enabled the signal is emitted at most once per
+ vsync interval in a continuously animating scene.
+ */
+
+/*!
+ \fn void QQuickWindow::sceneGraphInitialized()
+
+ This signal is emitted when the scene graph has been initialized.
+
+ This signal will be emitted from the scene graph rendering thread.
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::sceneGraphInitialized()
+ \internal
+ */
+
+/*!
+ \fn void QQuickWindow::sceneGraphInvalidated()
+
+ This signal is emitted when the scene graph has been invalidated.
+
+ This signal implies that the graphics rendering context used
+ has been invalidated and all user resources tied to that context
+ should be released.
+
+ When rendering with OpenGL, the QOpenGLContext of this window will
+ be bound when this function is called. The only exception is if
+ the native OpenGL has been destroyed outside Qt's control, for
+ instance through EGL_CONTEXT_LOST.
+
+ This signal will be emitted from the scene graph rendering thread.
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::sceneGraphInvalidated()
+ \internal
+ */
+
+/*!
+ \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message)
+
+ This signal is emitted when an \a error occurred during scene graph initialization.
+
+ Applications should connect to this signal if they wish to handle errors,
+ like graphics context creation failures, in a custom way. When no slot is
+ connected to the signal, the behavior will be different: Quick will print
+ the \a message, or show a message box, and terminate the application.
+
+ This signal will be emitted from the GUI thread.
+
+ \since 5.3
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::sceneGraphError(SceneGraphError error, QString message)
+
+ This signal is emitted when an \a error occurred during scene graph initialization.
+
+ You can implement onSceneGraphError(error, message) to handle errors,
+ such as graphics context creation failures, in a custom way.
+ If no handler is connected to this signal, Quick will print the \a message,
+ or show a message box, and terminate the application.
+
+ \since 5.3
+ */
+
+/*!
+ \class QQuickCloseEvent
+ \internal
+ \since 5.1
+
+ \inmodule QtQuick
+
+ \brief Notification that a \l QQuickWindow is about to be closed
+*/
+/*!
+ \qmltype CloseEvent
+ \instantiates QQuickCloseEvent
+ \inqmlmodule QtQuick.Window
+ \ingroup qtquick-visual
+ \brief Notification that a \l Window is about to be closed.
+ \since 5.1
+
+ Notification that a window is about to be closed by the windowing system
+ (e.g. the user clicked the title bar close button). The CloseEvent contains
+ an accepted property which can be set to false to abort closing the window.
+
+ \sa QQuickWindow::closing()
+*/
+
+/*!
+ \qmlproperty bool CloseEvent::accepted
+
+ This property indicates whether the application will allow the user to
+ close the window. It is true by default.
+*/
+
+/*!
+ \fn void QQuickWindow::closing(QQuickCloseEvent *close)
+ \since 5.1
+
+ This signal is emitted when the window receives the event \a close from
+ the windowing system.
+
+ On \macOs, Qt will create a menu item \c Quit if there is no menu item
+ whose text is "quit" or "exit". This menu item calls the \c QCoreApplication::quit
+ signal, not the \c QQuickWindow::closing() signal.
+
+ \sa {QMenuBar as a Global Menu Bar}
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::closing(CloseEvent close)
+ \since 5.1
+
+ This signal is emitted when the user tries to close the window.
+
+ This signal includes a \a close parameter. The \c {close.accepted}
+ property is true by default so that the window is allowed to close; but you
+ can implement an \c onClosing handler and set \c {close.accepted = false} if
+ you need to do something else before the window can be closed.
+ */
+
+/*!
+ Sets the render target for this window to be \a target.
+
+ A QQuickRenderTarget serves as an opaque handle for a renderable native
+ object, most commonly a 2D texture, and associated metadata, such as the
+ size in pixels.
+
+ A default constructed QQuickRenderTarget means no redirection. A valid
+ \a target, created via one of the static QQuickRenderTarget factory functions,
+ on the other hand, enables redirection of the rendering of the Qt Quick
+ scene: it will no longer target the color buffers for the surface
+ associated with the window, but rather the textures or other graphics
+ objects specified in \a target.
+
+ For example, assuming the scenegraph is using Vulkan to render, one can
+ redirect its output into a \c VkImage. For graphics APIs like Vulkan, the
+ image layout must be provided as well. QQuickRenderTarget instances are
+ implicitly shared and are copyable and can be passed by value. They do not
+ own the associated native objects (such as, the VkImage in the example),
+ however.
+
+ \badcode
+ QQuickRenderTarget rt = QQuickRenderTarget::fromVulkanImage(vulkanImage, VK_IMAGE_LAYOUT_PREINITIALIZED, pixelSize);
+ quickWindow->setRenderTarget(rt);
+ \endcode
+
+ This function is very often used in combination with QQuickRenderControl
+ and an invisible QQuickWindow, in order to render Qt Quick content into a
+ texture, without creating an on-screen native window for this QQuickWindow.
+
+ When the desired target, or associated data, such as the size, changes,
+ call this function with a new QQuickRenderTarget. Constructing
+ QQuickRenderTarget instances and calling this function is cheap, but be
+ aware that setting a new \a target with a different native object or other
+ data may lead to potentially expensive initialization steps when the
+ scenegraph is about to render the next frame. Therefore change the target
+ only when necessary.
+
+ \note This function should not be used when using the \c software backend.
+ Instead, use grabWindow() to render the content into a QImage.
+
+ \note The window does not take ownership of any native objects referenced
+ in \a target.
+
+ \note It is the caller's responsibility to ensure the native objects
+ referred to in \a target are valid for the scenegraph renderer too. For
+ instance, with Vulkan, Metal, and Direct3D this implies that the texture or
+ image is created on the same graphics device that is used by the scenegraph
+ internally. Therefore, when texture objects created on an already existing
+ device or context are involved, this function is often used in combination
+ with setGraphicsDevice().
+
+ \note With graphics APIs where relevant, the application must pay attention
+ to image layout transitions performed by the scenegraph. For example, once
+ a VkImage is associated with the scenegraph by calling this function, its
+ layout will transition to \c VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL when
+ rendering a frame.
+
+ \warning This function can only be called from the thread doing the
+ rendering.
+
+ \since 6.0
+
+ \sa QQuickRenderControl, setGraphicsDevice(), setGraphicsApi()
+ */
+void QQuickWindow::setRenderTarget(const QQuickRenderTarget &target)
+{
+ Q_D(QQuickWindow);
+ if (target != d->customRenderTarget) {
+ d->customRenderTarget = target;
+ d->redirect.renderTargetDirty = true;
+ }
+}
+
+/*!
+ \return the QQuickRenderTarget passed to setRenderTarget(), or a default
+ constructed one otherwise
+
+ \since 6.0
+
+ \sa setRenderTarget()
+ */
+QQuickRenderTarget QQuickWindow::renderTarget() const
+{
+ Q_D(const QQuickWindow);
+ return d->customRenderTarget;
+}
+
+/*!
+ Grabs the contents of the window and returns it as an image.
+
+ It is possible to call the grabWindow() function when the window is not
+ visible. This requires that the window is \l{QWindow::create()} {created}
+ and has a valid size and that no other QQuickWindow instances are rendering
+ in the same process.
+
+ \note When using this window in combination with QQuickRenderControl, the
+ result of this function is an empty image, unless the \c software backend
+ is in use. This is because when redirecting the output to an
+ application-managed graphics resource (such as, a texture) by using
+ QQuickRenderControl and setRenderTarget(), the application is better suited
+ for managing and executing an eventual read back operation, since it is in
+ full control of the resource to begin with.
+
+ \warning Calling this function will cause performance problems.
+
+ \warning This function can only be called from the GUI thread.
+ */
+QImage QQuickWindow::grabWindow()
+{
+ Q_D(QQuickWindow);
+
+ if (!isVisible() && !d->renderControl) {
+ // backends like software can grab regardless of the window state
+ if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose))
+ return d->windowManager->grab(this);
+
+ if (!isSceneGraphInitialized() && QSGRhiSupport::instance()->isRhiEnabled()) {
+ // We do not have rendering up and running. Forget the render loop,
+ // do a frame completely offscreen and synchronously into a
+ // texture. This can be *very* slow due to all the device/context
+ // and resource initialization but the documentation warns for it,
+ // and is still important for some use cases.
+ Q_ASSERT(!d->rhi);
+ return QSGRhiSupport::instance()->grabOffscreen(this);
+ }
+ }
+
+ // The common case: we have an exposed window with an initialized
+ // scenegraph, meaning we can request grabbing via the render loop, or we
+ // are not targeting the window, in which case the request is to be
+ // forwarded to the rendercontrol.
+ if (d->renderControl)
+ return QQuickRenderControlPrivate::get(d->renderControl)->grab();
+ else if (d->windowManager)
+ return d->windowManager->grab(this);
+ return QImage();
+}
+
+/*!
+ Returns an incubation controller that splices incubation between frames
+ for this window. QQuickView automatically installs this controller for you,
+ otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}.
+
+ The controller is owned by the window and will be destroyed when the window
+ is deleted.
+*/
+QQmlIncubationController *QQuickWindow::incubationController() const
+{
+ Q_D(const QQuickWindow);
+
+ if (!d->windowManager)
+ return nullptr; // TODO: make sure that this is safe
+
+ if (!d->incubationController)
+ d->incubationController = new QQuickWindowIncubationController(d->windowManager);
+ return d->incubationController;
+}
+
+
+
+/*!
+ \enum QQuickWindow::CreateTextureOption
+
+ The CreateTextureOption enums are used to customize a texture is wrapped.
+
+ \value TextureHasAlphaChannel The texture has an alpha channel and should
+ be drawn using blending.
+
+ \value TextureHasMipmaps The texture has mipmaps and can be drawn with
+ mipmapping enabled.
+
+ \value TextureOwnsGLTexture As of Qt 6.0, this flag is not used in practice
+ and is ignored. Native graphics resource ownership is not transferable to
+ the wrapping QSGTexture, because Qt Quick may not have the necessary details
+ on how such an object and the associated memory should be freed.
+
+ \value TextureCanUseAtlas The image can be uploaded into a texture atlas.
+
+ \value TextureIsOpaque The texture will return false for
+ QSGTexture::hasAlphaChannel() and will not be blended. This flag was added
+ in Qt 5.6.
+
+ */
+
+/*!
+ \enum QQuickWindow::SceneGraphError
+
+ This enum describes the error in a sceneGraphError() signal.
+
+ \value ContextNotAvailable graphics context creation failed. This typically means that
+ no suitable OpenGL implementation was found, for example because no graphics drivers
+ are installed and so no OpenGL 2 support is present. On mobile and embedded boards
+ that use OpenGL ES such an error is likely to indicate issues in the windowing system
+ integration and possibly an incorrect configuration of Qt.
+
+ \since 5.3
+ */
+
+/*!
+ \enum QQuickWindow::TextRenderType
+ \since 5.10
+
+ This enum describes the default render type of text-like elements in Qt
+ Quick (\l Text, \l TextInput, etc.).
+
+ Select NativeTextRendering if you prefer text to look native on the target
+ platform and do not require advanced features such as transformation of the
+ text. Using such features in combination with the NativeTextRendering
+ render type will lend poor and sometimes pixelated results.
+
+ \value QtTextRendering Use Qt's own rasterization algorithm.
+
+ \value NativeTextRendering Use the operating system's native rasterizer for text.
+*/
+
+/*!
+ \fn void QQuickWindow::beforeSynchronizing()
+
+ This signal is emitted before the scene graph is synchronized with the QML state.
+
+ Even though the signal is emitted from the scene graph rendering thread,
+ the GUI thread is guaranteed to be blocked, like it is in
+ QQuickItem::updatePaintNode(). Therefore, it is safe to access GUI thread
+ thread data in a slot or lambda that is connected with
+ Qt::DirectConnection.
+
+ This signal can be used to do any preparation required before calls to
+ QQuickItem::updatePaintNode().
+
+ When using OpenGL, the QOpenGLContext used for rendering by the scene graph
+ will be bound at this point.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when returning
+ from the connected slot can interfere with the scene graph's rendering.
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::beforeSynchronizing()
+ \internal
+*/
+
+/*!
+ \fn void QQuickWindow::afterSynchronizing()
+
+ This signal is emitted after the scene graph is synchronized with the QML state.
+
+ This signal can be used to do preparation required after calls to
+ QQuickItem::updatePaintNode(), while the GUI thread is still locked.
+
+ When using OpenGL, the QOpenGLContext used for rendering by the scene graph
+ will be bound at this point.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when returning
+ from the connected slot can interfere with the scene graph's rendering.
+
+ \since 5.3
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::afterSynchronizing()
+ \internal
+ \since 5.3
+ */
+
+/*!
+ \fn void QQuickWindow::beforeRendering()
+
+ This signal is emitted after the preparations for the frame have been done,
+ meaning there is a command buffer in recording mode, where applicable. If
+ desired, the slot function connected to this signal can query native
+ resources like the command before via QSGRendererInterface. Note however
+ that the recording of the main render pass is not yet started at this point
+ and it is not possible to add commands within that pass. Starting a pass
+ means clearing the color, depth, and stencil buffers so it is not possible
+ to achieve an underlay type of rendering by just connecting to this
+ signal. Rather, connect to beforeRenderPassRecording(). However, connecting
+ to this signal is still important if the recording of copy type of commands
+ is desired since those cannot be enqueued within a render pass.
+
+ When using OpenGL, the QOpenGLContext used for rendering by the scene graph
+ will be bound at this point.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when returning
+ from the connected slot can interfere with the scene graph's rendering.
+
+ \sa rendererInterface(), {Scene Graph - OpenGL Under QML}, {Scene Graph - Metal Under QML},
+ {Scene Graph - Vulkan Under QML}, {Scene Graph - Direct3D 11 Under QML}
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::beforeRendering()
+ \internal
+*/
+
+/*!
+ \fn void QQuickWindow::afterRendering()
+
+ The signal is emitted after scene graph has added its commands to the
+ command buffer, which is not yet submitted to the graphics queue. If
+ desired, the slot function connected to this signal can query native
+ resources, like the command buffer, before via QSGRendererInterface. Note
+ however that the render pass (or passes) are already recorded at this point
+ and it is not possible to add more commands within the scenegraph's
+ pass. Instead, use afterRenderPassRecording() for that. This signal has
+ therefore limited use in Qt 6, unlike in Qt 5. Rather, it is the combination
+ of beforeRendering() and beforeRenderPassRecording(), or beforeRendering()
+ and afterRenderPassRecording(), that is typically used to achieve under- or
+ overlaying of the custom rendering.
+
+ When using OpenGL, the QOpenGLContext used for rendering by the scene graph
+ will be bound at this point.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific
+ states and leaving these enabled or set to non-default values when returning
+ from the connected slot can interfere with the scene graph's rendering.
+
+ \sa rendererInterface(), {Scene Graph - OpenGL Under QML}, {Scene Graph - Metal Under QML},
+ {Scene Graph - Vulkan Under QML}, {Scene Graph - Direct3D 11 Under QML}
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::afterRendering()
+ \internal
+ */
+
+/*!
+ \fn void QQuickWindow::beforeRenderPassRecording()
+
+ This signal is emitted before the scenegraph starts recording commands for
+ the main render pass. (Layers have their own passes and are fully recorded
+ by the time this signal is emitted.) The render pass is already active on
+ the command buffer when the signal is emitted.
+
+ This signal is emitted later than beforeRendering() and it guarantees that
+ not just the frame, but also the recording of the scenegraph's main render
+ pass is active. This allows inserting commands without having to generate an
+ entire, separate render pass (which would typically clear the attached
+ images). The native graphics objects can be queried via
+ QSGRendererInterface.
+
+ \note Resource updates (uploads, copies) typically cannot be enqueued from
+ within a render pass. Therefore, more complex user rendering will need to
+ connect to both beforeRendering() and this signal.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \sa rendererInterface()
+
+ \since 5.14
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::beforeRenderPassRecording()
+ \internal
+ \since 5.14
+*/
+
+/*!
+ \fn void QQuickWindow::afterRenderPassRecording()
+
+ This signal is emitted after the scenegraph has recorded the commands for
+ its main render pass, but the pass is not yet finalized on the command
+ buffer.
+
+ This signal is emitted earlier than afterRendering(), and it guarantees that
+ not just the frame but also the recording of the scenegraph's main render
+ pass is still active. This allows inserting commands without having to
+ generate an entire, separate render pass (which would typically clear the
+ attached images). The native graphics objects can be queried via
+ QSGRendererInterface.
+
+ \note Resource updates (uploads, copies) typically cannot be enqueued from
+ within a render pass. Therefore, more complex user rendering will need to
+ connect to both beforeRendering() and this signal.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \sa rendererInterface()
+
+ \since 5.14
+*/
+
+/*!
+ \fn void QQuickWindow::beforeFrameBegin()
+
+ This signal is emitted before the scene graph starts preparing the frame.
+ This precedes signals like beforeSynchronizing() or beforeRendering(). It is
+ the earliest signal that is emitted by the scene graph rendering thread
+ when starting to prepare a new frame.
+
+ This signal is relevant for lower level graphics frameworks that need to
+ execute certain operations, such as resource cleanup, at a stage where Qt
+ Quick has not initiated the recording of a new frame via the underlying
+ rendering hardware interface APIs.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \since 6.0
+
+ \sa afterFrameEnd(), rendererInterface()
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::beforeFrameBegin()
+ \internal
+*/
+
+/*!
+ \fn void QQuickWindow::afterFrameEnd()
+
+ This signal is emitted when the scene graph has submitted a frame. This is
+ emitted after all other related signals, such as afterRendering(). It is
+ the last signal that is emitted by the scene graph rendering thread when
+ rendering a frame.
+
+ \note Unlike frameSwapped(), this signal is guaranteed to be emitted also
+ when the Qt Quick output is redirected via QQuickRenderControl.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \since 6.0
+
+ \sa beforeFrameBegin(), rendererInterface()
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::afterFrameEnd()
+ \internal
+*/
+
+/*!
+ \qmlsignal QtQuick.Window::Window::afterRenderPassRecording()
+ \internal
+ \since 5.14
+*/
+
+/*!
+ \fn void QQuickWindow::afterAnimating()
+
+ This signal is emitted on the GUI thread before requesting the render thread to
+ perform the synchronization of the scene graph.
+
+ Unlike the other similar signals, this one is emitted on the GUI thread
+ instead of the render thread. It can be used to synchronize external
+ animation systems with the QML content. At the same time this means that
+ this signal is not suitable for triggering graphics operations.
+
+ \since 5.3
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::afterAnimating()
+
+ This signal is emitted on the GUI thread before requesting the render thread to
+ perform the synchronization of the scene graph.
+
+ You can implement onAfterAnimating to do additional processing after each animation step.
+
+ \since 5.3
+ */
+
+/*!
+ \fn void QQuickWindow::sceneGraphAboutToStop()
+
+ This signal is emitted on the render thread when the scene graph is
+ about to stop rendering. This happens usually because the window
+ has been hidden.
+
+ Applications may use this signal to release resources, but should be
+ prepared to reinstantiated them again fast. The scene graph and the
+ graphics context are not released at this time.
+
+ \warning This signal is emitted from the scene graph rendering thread. If your
+ slot function needs to finish before execution continues, you must make sure that
+ the connection is direct (see Qt::ConnectionType).
+
+ \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the
+ graphics context in the same state as it was when the signal handler was entered.
+ Failing to do so can result in the scene not rendering properly.
+
+ \sa sceneGraphInvalidated()
+ \since 5.3
+ */
+
+/*!
+ \qmlsignal QtQuick.Window::Window::sceneGraphAboutToStop()
+ \internal
+ \since 5.3
+ */
+
+/*!
+ \overload
+ */
+
+QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
+{
+ return createTextureFromImage(image, {});
+}
+
+
+/*!
+ Creates a new QSGTexture from the supplied \a image. If the image has an
+ alpha channel, the corresponding texture will have an alpha channel.
+
+ The caller of the function is responsible for deleting the returned texture.
+ The underlying native texture object is then destroyed together with the
+ QSGTexture.
+
+ When \a options contains TextureCanUseAtlas, the engine may put the image
+ into a texture atlas. Textures in an atlas need to rely on
+ QSGTexture::normalizedTextureSubRect() for their geometry and will not
+ support QSGTexture::Repeat. Other values from CreateTextureOption are
+ ignored.
+
+ When \a options contains TextureIsOpaque, the engine will create an RGB
+ texture which returns false for QSGTexture::hasAlphaChannel(). Opaque
+ textures will in most cases be faster to render. When this flag is not set,
+ the texture will have an alpha channel based on the image's format.
+
+ When \a options contains TextureHasMipmaps, the engine will create a
+ texture which can use mipmap filtering. Mipmapped textures can not be in
+ an atlas.
+
+ When the scene graph uses OpenGL, the returned texture will be using \c
+ GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. With
+ other graphics APIs, the texture format is typically \c RGBA8. Reimplement
+ QSGTexture to create textures with different parameters.
+
+ \warning This function will return 0 if the scene graph has not yet been
+ initialized.
+
+ \warning The returned texture is not memory managed by the scene graph and
+ must be explicitly deleted by the caller on the rendering thread.
+ This is achieved by deleting the texture from a QSGNode destructor
+ or by using deleteLater() in the case where the texture already has affinity
+ to the rendering thread.
+
+ This function can be called from any thread.
+
+ \sa sceneGraphInitialized(), QSGTexture
+ */
+
+QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const
+{
+ Q_D(const QQuickWindow);
+ if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid()
+ return nullptr;
+ uint flags = 0;
+ if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas;
+ if (options & TextureHasMipmaps) flags |= QSGRenderContext::CreateTexture_Mipmap;
+ if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha;
+ return d->context->createTexture(image, flags);
+}
+
+QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeObjectHandle,
+ int nativeLayout,
+ const QSize &size,
+ QQuickWindow::CreateTextureOptions options,
+ TextureFromNativeTextureFlags flags) const
+{
+ if (!rhi)
+ return nullptr;
+
+ QSGPlainTexture *texture = new QSGPlainTexture;
+ texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayout,
+ size, options, flags);
+ texture->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel);
+ // note that the QRhiTexture does not (and cannot) own the native object
+ texture->setOwnsTexture(true); // texture meaning the QRhiTexture here, not the native object
+ texture->setTextureSize(size);
+ return texture;
+}
+
+/*!
+ \qmlproperty color Window::color
+
+ The background color for the window.
+
+ Setting this property is more efficient than using a separate Rectangle.
+*/
+
+/*!
+ \property QQuickWindow::color
+ \brief The color used to clear the color buffer at the beginning of each frame.
+
+ By default, the clear color is white.
+
+ \sa setDefaultAlphaBuffer()
+ */
+
+void QQuickWindow::setColor(const QColor &color)
+{
+ Q_D(QQuickWindow);
+ if (color == d->clearColor)
+ return;
+
+ if (color.alpha() != d->clearColor.alpha()) {
+ QSurfaceFormat fmt = requestedFormat();
+ if (color.alpha() < 255)
+ fmt.setAlphaBufferSize(8);
+ else
+ fmt.setAlphaBufferSize(-1);
+ setFormat(fmt);
+ }
+ d->clearColor = color;
+ emit colorChanged(color);
+ update();
+}
+
+QColor QQuickWindow::color() const
+{
+ return d_func()->clearColor;
+}
+
+/*!
+ \brief Returns whether to use alpha transparency on newly created windows.
+
+ \since 5.1
+ \sa setDefaultAlphaBuffer()
+ */
+bool QQuickWindow::hasDefaultAlphaBuffer()
+{
+ return QQuickWindowPrivate::defaultAlphaBuffer;
+}
+
+/*!
+ \brief \a useAlpha specifies whether to use alpha transparency on newly created windows.
+ \since 5.1
+
+ In any application which expects to create translucent windows, it's necessary to set
+ this to true before creating the first QQuickWindow. The default value is false.
+
+ \sa hasDefaultAlphaBuffer()
+ */
+void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
+{
+ QQuickWindowPrivate::defaultAlphaBuffer = useAlpha;
+}
+
+/*!
+ \struct QQuickWindow::GraphicsStateInfo
+ \inmodule QtQuick
+ \since 5.14
+
+ \brief Describes some of the RHI's graphics state at the point of a
+ \l{QQuickWindow::beginExternalCommands()}{beginExternalCommands()} call.
+ */
+
+/*!
+ \return a reference to a GraphicsStateInfo struct describing some of the
+ RHI's internal state, in particular, the double or tripple buffering status
+ of the backend (such as, the Vulkan or Metal integrations). This is
+ relevant when the underlying graphics APIs is Vulkan or Metal, and the
+ external rendering code wishes to perform double or tripple buffering of
+ its own often-changing resources, such as, uniform buffers, in order to
+ avoid stalling the pipeline.
+ */
+const QQuickWindow::GraphicsStateInfo &QQuickWindow::graphicsStateInfo()
+{
+ Q_D(QQuickWindow);
+ if (d->rhi) {
+ d->rhiStateInfo.currentFrameSlot = d->rhi->currentFrameSlot();
+ d->rhiStateInfo.framesInFlight = d->rhi->resourceLimit(QRhi::FramesInFlight);
+ }
+ return d->rhiStateInfo;
+}
+
+/*!
+ When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene
+ graph rendering, it is necessary to call this function before recording
+ commands to the command buffer used by the scene graph to render its main
+ render pass. This is to avoid clobbering state.
+
+ In practice this function is often called from a slot connected to the
+ beforeRenderPassRecording() or afterRenderPassRecording() signals.
+
+ The function does not need to be called when recording commands to the
+ application's own command buffer (such as, a VkCommandBuffer or
+ MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the
+ application, not retrieved from the scene graph). With graphics APIs where
+ no native command buffer concept is exposed (OpenGL, Direct 3D 11),
+ beginExternalCommands() and endExternalCommands() together provide a
+ replacement for the Qt 5 resetOpenGLState() function.
+
+ Calling this function and endExternalCommands() is not necessary within the
+ \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
+ because the scene graph performs the necessary steps implicitly for render
+ nodes.
+
+ Native graphics objects (such as, graphics device, command buffer or
+ encoder) are accessible via QSGRendererInterface::getResource().
+
+ \warning Watch out for the fact that
+ QSGRendererInterface::CommandListResource may return a different object
+ between beginExternalCommands() - endExternalCommands(). This can happen
+ when the underlying implementation provides a dedicated secondary command
+ buffer for recording external graphics commands within a render pass.
+ Therefore, always query CommandListResource after calling this function. Do
+ not attempt to reuse an object from an earlier query.
+
+ \note When the scenegraph is using OpenGL, pay attention to the fact that
+ the OpenGL state in the context can have arbitrary settings, and this
+ function does not perform any resetting of the state back to defaults.
+
+ \sa endExternalCommands(), QQuickOpenGLUtils::resetOpenGLState()
+
+ \since 5.14
+ */
+void QQuickWindow::beginExternalCommands()
+{
+ Q_D(QQuickWindow);
+ if (d->rhi && d->context && d->context->isValid()) {
+ QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context);
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ if (cb)
+ cb->beginExternal();
+ }
+}
+
+/*!
+ When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene
+ graph rendering, it is necessary to call this function after recording
+ commands to the command buffer used by the scene graph to render its main
+ render pass. This is to avoid clobbering state.
+
+ In practice this function is often called from a slot connected to the
+ beforeRenderPassRecording() or afterRenderPassRecording() signals.
+
+ The function does not need to be called when recording commands to the
+ application's own command buffer (such as, a VkCommandBuffer or
+ MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the
+ application, not retrieved from the scene graph). With graphics APIs where
+ no native command buffer concept is exposed (OpenGL, Direct 3D 11),
+ beginExternalCommands() and endExternalCommands() together provide a
+ replacement for the Qt 5 resetOpenGLState() function.
+
+ Calling this function and beginExternalCommands() is not necessary within the
+ \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
+ because the scene graph performs the necessary steps implicitly for render
+ nodes.
+
+ \sa beginExternalCommands(), QQuickOpenGLUtils::resetOpenGLState()
+
+ \since 5.14
+ */
+void QQuickWindow::endExternalCommands()
+{
+ Q_D(QQuickWindow);
+ if (d->rhi && d->context && d->context->isValid()) {
+ QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context);
+ QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
+ if (cb)
+ cb->endExternal();
+ }
+}
+
+/*!
+ \qmlproperty string Window::title
+
+ The window's title in the windowing system.
+
+ The window title might appear in the title area of the window decorations,
+ depending on the windowing system and the window flags. It might also
+ be used by the windowing system to identify the window in other contexts,
+ such as in the task switcher.
+ */
+
+/*!
+ \qmlproperty Qt::WindowModality Window::modality
+
+ The modality of the window.
+
+ A modal window prevents other windows from receiving input events.
+ Possible values are Qt.NonModal (the default), Qt.WindowModal,
+ and Qt.ApplicationModal.
+ */
+
+/*!
+ \qmlproperty Qt::WindowFlags Window::flags
+
+ The window flags of the window.
+
+ The window flags control the window's appearance in the windowing system,
+ whether it's a dialog, popup, or a regular window, and whether it should
+ have a title bar, etc.
+
+ The flags which you read from this property might differ from the ones
+ that you set if the requested flags could not be fulfilled.
+
+ \sa Qt::WindowFlags
+ */
+
+/*!
+ \qmlattachedproperty Window Window::window
+ \since 5.7
+
+ This attached property holds the item's window.
+ The Window attached property can be attached to any Item.
+*/
+
+/*!
+ \qmlattachedproperty int Window::width
+ \qmlattachedproperty int Window::height
+ \since 5.5
+
+ These attached properties hold the size of the item's window.
+ The Window attached property can be attached to any Item.
+*/
+
+/*!
+ \qmlproperty int Window::x
+ \qmlproperty int Window::y
+ \qmlproperty int Window::width
+ \qmlproperty int Window::height
+
+ Defines the window's position and size.
+
+ The (x,y) position is relative to the \l Screen if there is only one,
+ or to the virtual desktop (arrangement of multiple screens).
+
+ \qml
+ Window { x: 100; y: 100; width: 100; height: 100 }
+ \endqml
+
+ \image screen-and-window-dimensions.jpg
+ */
+
+/*!
+ \qmlproperty int Window::minimumWidth
+ \qmlproperty int Window::minimumHeight
+ \since 5.1
+
+ Defines the window's minimum size.
+
+ This is a hint to the window manager to prevent resizing below the specified
+ width and height.
+ */
+
+/*!
+ \qmlproperty int Window::maximumWidth
+ \qmlproperty int Window::maximumHeight
+ \since 5.1
+
+ Defines the window's maximum size.
+
+ This is a hint to the window manager to prevent resizing above the specified
+ width and height.
+ */
+
+/*!
+ \qmlproperty bool Window::visible
+
+ Whether the window is visible on the screen.
+
+ Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}.
+
+ \sa visibility
+ */
+
+/*!
+ \qmlproperty QWindow::Visibility Window::visibility
+
+ The screen-occupation state of the window.
+
+ Visibility is whether the window should appear in the windowing system as
+ normal, minimized, maximized, fullscreen or hidden.
+
+ To set the visibility to \l {QWindow::}{AutomaticVisibility} means to give the
+ window a default visible state, which might be \l {QWindow::}{FullScreen} or
+ \l {QWindow::}{Windowed} depending on the platform. However when reading the
+ visibility property you will always get the actual state, never
+ \c AutomaticVisibility.
+
+ When a window is not visible its visibility is Hidden, and setting
+ visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false.
+
+ \sa visible
+ \since 5.1
+ */
+
+/*!
+ \qmlattachedproperty QWindow::Visibility Window::visibility
+ \since 5.4
+
+ This attached property holds whether the window is currently shown
+ in the windowing system as normal, minimized, maximized, fullscreen or
+ hidden. The \c Window attached property can be attached to any Item. If the
+ item is not shown in any window, the value will be \l {QWindow::}{Hidden}.
+
+ \sa visible, visibility
+*/
+
+/*!
+ \qmlproperty Item Window::contentItem
+ \readonly
+ \brief The invisible root item of the scene.
+*/
+
+/*!
+ \qmlproperty Qt::ScreenOrientation Window::contentOrientation
+
+ This is a hint to the window manager in case it needs to display
+ additional content like popups, dialogs, status bars, or similar
+ in relation to the window.
+
+ The recommended orientation is \l {Screen::orientation}{Screen.orientation}, but
+ an application doesn't have to support all possible orientations,
+ and thus can opt to ignore the current screen orientation.
+
+ The difference between the window and the content orientation
+ determines how much to rotate the content by.
+
+ The default value is Qt::PrimaryOrientation.
+
+ \sa Screen
+
+ \since 5.1
+ */
+
+/*!
+ \qmlproperty real Window::opacity
+
+ The opacity of the window.
+
+ If the windowing system supports window opacity, this can be used to fade the
+ window in and out, or to make it semitransparent.
+
+ A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below
+ is treated as fully transparent. Values inbetween represent varying levels of
+ translucency between the two extremes.
+
+ The default value is 1.0.
+
+ \since 5.1
+ */
+
+/*!
+ \qmlproperty variant Window::screen
+
+ The screen with which the window is associated.
+
+ If specified before showing a window, will result in the window being shown
+ on that screen, unless an explicit window position has been set. The value
+ must be an element from the Qt.application.screens array.
+
+ \note To ensure that the window is associated with the desired screen when
+ the underlying native window is created, make sure this property is set as
+ early as possible and that the setting of its value is not deferred. This
+ can be particularly important on embedded platforms without a windowing system,
+ where only one window per screen is allowed at a time. Setting the screen after
+ a window has been created does not move the window if the new screen is part of
+ the same virtual desktop as the old screen.
+
+ \since 5.9
+
+ \sa QWindow::setScreen(), QWindow::screen(), QScreen, {QtQml::Qt::application}{Qt.application}
+ */
+
+/*!
+ \qmlproperty QWindow Window::transientParent
+ \since 5.13
+
+ The window for which this window is a transient pop-up.
+
+ This is a hint to the window manager that this window is a dialog or pop-up
+ on behalf of the transient parent. It usually means that the transient
+ window will be centered over its transient parent when it is initially
+ shown, that minimizing the parent window will also minimize the transient
+ window, and so on; however results vary somewhat from platform to platform.
+
+ Normally if you declare a Window inside an Item or inside another Window,
+ this relationship is deduced automatically. In that case, if you declare
+ this window's \l visible property \c true, it will not actually be shown
+ until the \c transientParent window is shown.
+
+ However if you set this property, then Qt Quick will no longer wait until
+ the \c transientParent window is shown before showing this window. If you
+ want to to be able to show a transient window independently of the "parent"
+ Item or Window within which it was declared, you can remove that
+ relationship by setting \c transientParent to \c null:
+
+ \qml
+ import QtQuick.Window 2.13
+
+ Window {
+ // visible is false by default
+ Window {
+ transientParent: null
+ visible: true
+ }
+ }
+ \endqml
+
+ In order to cause the window to be centered above its transient parent by
+ default, depending on the window manager, it may also be necessary to set
+ the \l Window::flags property with a suitable \l Qt::WindowType (such as
+ \c Qt::Dialog).
+*/
+
+/*!
+ \property QQuickWindow::transientParent
+ \brief The window for which this window is a transient pop-up.
+ \since 5.13
+
+ This is a hint to the window manager that this window is a dialog or pop-up
+ on behalf of the transient parent, which may be any kind of \l QWindow.
+
+ In order to cause the window to be centered above its transient parent by
+ default, depending on the window manager, it may also be necessary to set
+ the \l flags property with a suitable \l Qt::WindowType (such as \c Qt::Dialog).
+
+ \sa parent()
+ */
+
+/*!
+ \qmlproperty Item Window::activeFocusItem
+ \since 5.1
+
+ The item which currently has active focus or \c null if there is
+ no item with active focus.
+ */
+
+/*!
+ \qmlattachedproperty Item Window::activeFocusItem
+ \since 5.4
+
+ This attached property holds the item which currently has active focus or
+ \c null if there is no item with active focus. The Window attached property
+ can be attached to any Item.
+*/
+
+/*!
+ \qmlproperty bool Window::active
+ \since 5.1
+
+ The active status of the window.
+
+ \sa requestActivate()
+ */
+
+/*!
+ \qmlattachedproperty bool Window::active
+ \since 5.4
+
+ This attached property tells whether the window is active. The Window
+ attached property can be attached to any Item.
+
+ Here is an example which changes a label to show the active state of the
+ window in which it is shown:
+
+ \qml
+ import QtQuick 2.4
+ import QtQuick.Window 2.2
+
+ Text {
+ text: Window.active ? "active" : "inactive"
+ }
+ \endqml
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::requestActivate()
+ \since 5.1
+
+ Requests the window to be activated, i.e. receive keyboard focus.
+ */
+
+/*!
+ \qmlmethod QtQuick::Window::alert(int msec)
+ \since 5.1
+
+ Causes an alert to be shown for \a msec milliseconds. If \a msec is \c 0
+ (the default), then the alert is shown indefinitely until the window
+ becomes active again.
+
+ In alert state, the window indicates that it demands attention, for example
+ by flashing or bouncing the taskbar entry.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::close()
+
+ Closes the window.
+
+ When this method is called, or when the user tries to close the window by
+ its title bar button, the \l closing signal will be emitted. If there is no
+ handler, or the handler does not revoke permission to close, the window
+ will subsequently close. If the QGuiApplication::quitOnLastWindowClosed
+ property is \c true, and there are no other windows open, the application
+ will quit.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::raise()
+
+ Raises the window in the windowing system.
+
+ Requests that the window be raised to appear above other windows.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::lower()
+
+ Lowers the window in the windowing system.
+
+ Requests that the window be lowered to appear below other windows.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::show()
+
+ Shows the window.
+
+ This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(),
+ depending on the platform's default behavior for the window type and flags.
+
+ \sa showFullScreen(), showMaximized(), showNormal(), hide(), QQuickItem::flags()
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::hide()
+
+ Hides the window.
+
+ Equivalent to setting \l visible to \c false or \l visibility to \l {QWindow::}{Hidden}.
+
+ \sa show()
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::showMinimized()
+
+ Shows the window as minimized.
+
+ Equivalent to setting \l visibility to \l {QWindow::}{Minimized}.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::showMaximized()
+
+ Shows the window as maximized.
+
+ Equivalent to setting \l visibility to \l {QWindow::}{Maximized}.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::showFullScreen()
+
+ Shows the window as fullscreen.
+
+ Equivalent to setting \l visibility to \l {QWindow::}{FullScreen}.
+*/
+
+/*!
+ \qmlmethod QtQuick::Window::showNormal()
+
+ Shows the window as normal, i.e. neither maximized, minimized, nor fullscreen.
+
+ Equivalent to setting \l visibility to \l {QWindow::}{Windowed}.
+*/
+
+/*!
+ \enum QQuickWindow::RenderStage
+ \since 5.4
+
+ \value BeforeSynchronizingStage Before synchronization.
+ \value AfterSynchronizingStage After synchronization.
+ \value BeforeRenderingStage Before rendering.
+ \value AfterRenderingStage After rendering.
+ \value AfterSwapStage After the frame is swapped.
+ \value NoStage As soon as possible. This value was added in Qt 5.6.
+
+ \sa {Scene Graph and Rendering}
+ */
+
+/*!
+ \since 5.4
+
+ Schedules \a job to run when the rendering of this window reaches
+ the given \a stage.
+
+ This is a convenience to the equivalent signals in QQuickWindow for
+ "one shot" tasks.
+
+ The window takes ownership over \a job and will delete it when the
+ job is completed.
+
+ If rendering is shut down before \a job has a chance to run, the
+ job will be run and then deleted as part of the scene graph cleanup.
+ If the window is never shown and no rendering happens before the QQuickWindow
+ is destroyed, all pending jobs will be destroyed without their run()
+ method being called.
+
+ If the rendering is happening on a different thread, then the job
+ will happen on the rendering thread.
+
+ If \a stage is \l NoStage, \a job will be run at the earliest opportunity
+ whenever the render thread is not busy rendering a frame. If the window is
+ not exposed, and is not renderable, at the time the job is either posted or
+ handled, the job is deleted without executing the run() method. If a
+ non-threaded renderer is in use, the run() method of the job is executed
+ synchronously. When rendering with OpenGL, the OpenGL context is changed to
+ the renderer's context before executing any job, including \l NoStage jobs.
+
+ \note This function does not trigger rendering; the jobs targeting any other
+ stage than NoStage will be stored run until rendering is triggered elsewhere.
+ To force the job to run earlier, call QQuickWindow::update();
+
+ \sa beforeRendering(), afterRendering(), beforeSynchronizing(),
+ afterSynchronizing(), frameSwapped(), sceneGraphInvalidated()
+ */
+
+void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage)
+{
+ Q_D(QQuickWindow);
+
+ d->renderJobMutex.lock();
+ if (stage == BeforeSynchronizingStage) {
+ d->beforeSynchronizingJobs << job;
+ } else if (stage == AfterSynchronizingStage) {
+ d->afterSynchronizingJobs << job;
+ } else if (stage == BeforeRenderingStage) {
+ d->beforeRenderingJobs << job;
+ } else if (stage == AfterRenderingStage) {
+ d->afterRenderingJobs << job;
+ } else if (stage == AfterSwapStage) {
+ d->afterSwapJobs << job;
+ } else if (stage == NoStage) {
+ if (d->renderControl && d->rhi && d->rhi->thread() == QThread::currentThread()) {
+ job->run();
+ delete job;
+ } else if (isExposed()) {
+ d->windowManager->postJob(this, job);
+ } else {
+ delete job;
+ }
+ }
+ d->renderJobMutex.unlock();
+}
+
+void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs)
+{
+ renderJobMutex.lock();
+ QList<QRunnable *> jobList = *jobs;
+ jobs->clear();
+ renderJobMutex.unlock();
+
+ for (QRunnable *r : qAsConst(jobList)) {
+ r->run();
+ delete r;
+ }
+}
+
+void QQuickWindow::runJobsAfterSwap()
+{
+ Q_D(QQuickWindow);
+ d->runAndClearJobs(&d->afterSwapJobs);
+}
+
+/*!
+ * Returns the device pixel ratio for this window.
+ *
+ * This is different from QWindow::devicePixelRatio() in that it supports
+ * redirected rendering via QQuickRenderControl. When using a
+ * QQuickRenderControl, the QQuickWindow is often not created, meaning it is
+ * never shown and there is no underlying native window created in the
+ * windowing system. As a result, querying properties like the device pixel
+ * ratio cannot give correct results. Use this function instead.
+ *
+ * \sa QWindow::devicePixelRatio()
+ */
+qreal QQuickWindow::effectiveDevicePixelRatio() const
+{
+ QWindow *w = QQuickRenderControl::renderWindowFor(const_cast<QQuickWindow *>(this));
+ return w ? w->devicePixelRatio() : devicePixelRatio();
+}
+
+/*!
+ \return the current renderer interface. The value is always valid and is never null.
+
+ \note This function can be called at any time after constructing the
+ QQuickWindow, even while isSceneGraphInitialized() is still false. However,
+ some renderer interface functions, in particular
+ QSGRendererInterface::getResource() will not be functional until the
+ scenegraph is up and running. Backend queries, like
+ QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(),
+ will always be functional on the other hand.
+
+ \note The ownership of the returned pointer stays with Qt. The returned
+ instance may or may not be shared between different QQuickWindow instances,
+ depending on the scenegraph backend in use. Therefore applications are
+ expected to query the interface object for each QQuickWindow instead of
+ reusing the already queried pointer.
+
+ \sa QSGRenderNode, QSGRendererInterface
+
+ \since 5.8
+ */
+QSGRendererInterface *QQuickWindow::rendererInterface() const
+{
+ Q_D(const QQuickWindow);
+
+ // no context validity check - it is essential to be able to return a
+ // renderer interface instance before scenegraphInitialized() is emitted
+ // (depending on the backend, that can happen way too late for some of the
+ // rif use cases, like examining the graphics api or shading language in
+ // use)
+
+ return d->context->sceneGraphContext()->rendererInterface(d->context);
+}
+
+/*!
+ Requests the specified graphics \a api.
+
+ When the built-in, default graphics adaptation is used, \a api specifies
+ which graphics API (OpenGL, Vulkan, Metal, or Direct3D) the scene graph
+ should use to render. In addition, the \c software backend is built-in as
+ well, and can be requested by setting \a api to
+ QSGRendererInterface::Software.
+
+ Unlike setSceneGraphBackend(), which can only be used to request a given
+ backend (shipped either built-in or installed as dynamically loaded
+ plugins), this function works with the higher level concept of graphics
+ APIs. It covers the backends that ship with Qt Quick, and thus have
+ corresponding values in the QSGRendererInterface::GraphicsApi enum.
+
+ When this function is not called at all, and the equivalent environment
+ variable \c{QSG_RHI_BACKEND} is not set either, the scene graph will choose
+ the graphics API to use based on the platform.
+
+ This function becomes important in applications that are only prepared for
+ rendering with a given API. For example, if there is native OpenGL or
+ Vulkan rendering done by the application, it will want to ensure Qt Quick
+ is rendering using OpenGL or Vulkan too. Such applications are expected to
+ call this function early in their main() function.
+
+ \note The call to the function must happen before constructing the first
+ QQuickWindow in the application. The graphics API cannot be changed
+ afterwards.
+
+ \note When used in combination with QQuickRenderControl, this rule is
+ relaxed: it is possible to change the graphics API, but only when all
+ existing QQuickRenderControl and QQuickWindow instances have been
+ destroyed.
+
+ To query what graphics API the scene graph is using to render,
+ QSGRendererInterface::graphicsApi() after the scene graph
+ \l{QQuickWindow::isSceneGraphInitialized()}{has initialized}, which
+ typically happens either when the window becomes visible for the first time, or
+ when QQuickRenderControl::initialize() is called.
+
+ To switch back to the default behavior, where the scene graph chooses a
+ graphics API based on the platform and other conditions, set \a api to
+ QSGRendererInterface::Unknown.
+
+ \since 5.8
+ */
+void QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi api)
+{
+ // Special cases: these are different scenegraph backends.
+ switch (api) {
+ case QSGRendererInterface::Software:
+ setSceneGraphBackend(QStringLiteral("software"));
+ break;
+ case QSGRendererInterface::OpenVG:
+ setSceneGraphBackend(QStringLiteral("openvg"));
+ break;
+ default:
+ break;
+ }
+
+ // Standard case: tell the QRhi-based default adaptation what graphics api
+ // (QRhi backend) to use.
+ if (QSGRendererInterface::isApiRhiBased(api) || api == QSGRendererInterface::Unknown)
+ QSGRhiSupport::configure(api);
+}
+
+/*!
+ \return the graphics API that would be used by the scene graph if it was
+ initialized at this point in time.
+
+ The standard way to query the API used by the scene graph is to use
+ QSGRendererInterface::graphicsApi() once the scene graph has initialized,
+ for example when or after the sceneGraphInitialized() signal is emitted. In
+ that case one gets the true, real result, because then it is known that
+ everything was initialized correctly using that graphics API.
+
+ This is not always convenient. If the application needs to set up external
+ frameworks, or needs to work with setGraphicsDevice() in a manner that
+ depends on the scene graph's built in API selection logic, it is not always
+ feasiable to defer such operations until after the QQuickWindow has been
+ made visible or QQuickRenderControl::initialize() has been called.
+
+ Therefore, this static function is provided as a counterpart to
+ setGraphicsApi(): it can be called at any time, and the result reflects
+ what API the scene graph would choose if it was initialized at the point of
+ the call.
+
+ \note This static function is intended to be called on the main (GUI)
+ thread only. For querying the API when rendering, use QSGRendererInterface
+ since that object lives on the render thread.
+
+ \note This function does not take scene graph backends into account.
+
+ \since 6.0
+ */
+QSGRendererInterface::GraphicsApi QQuickWindow::graphicsApi()
+{
+ // Note that this applies the settings e.g. from the env vars
+ // (QSG_RHI_BACKEND) if it was not done at least once already. Whereas if
+ // setGraphicsApi() was called before, or the scene graph is already
+ // initialized, then this is just a simple query.
+ return QSGRhiSupport::instance()->graphicsApi();
+}
+
+/*!
+ Requests a Qt Quick scenegraph \a backend. Backends can either be built-in
+ or be installed in form of dynamically loaded plugins.
+
+ \overload
+
+ \note The call to the function must happen before constructing the first
+ QQuickWindow in the application. It cannot be changed afterwards.
+
+ If \a backend is invalid or an error occurs, the request is ignored.
+
+ \note Calling this function is equivalent to setting the
+ \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this
+ API is safer to use in applications that spawn other processes as there is
+ no need to worry about environment inheritance.
+
+ \since 5.8
+ */
+void QQuickWindow::setSceneGraphBackend(const QString &backend)
+{
+ QSGContext::setBackend(backend);
+}
+
+/*!
+ Returns the requested Qt Quick scenegraph backend.
+
+ \note The return value of this function may still be outdated by
+ subsequent calls to setSceneGraphBackend() until the first QQuickWindow in the
+ application has been constructed.
+
+ \note The value only reflects the request in the \c{QT_QUICK_BACKEND}
+ environment variable after a QQuickWindow has been constructed.
+
+ \since 5.9
+ */
+QString QQuickWindow::sceneGraphBackend()
+{
+ return QSGContext::backend();
+}
+
+/*!
+ Sets the graphics device objects for this window. The scenegraph will use
+ existing device, physical device, and other objects specified by \a device
+ instead of creating new ones.
+
+ This function is very often used in combination with QQuickRenderControl
+ and setRenderTarget(), in order to redirect Qt Quick rendering into a
+ texture.
+
+ A default constructed QQuickGraphicsDevice does not change the default
+ behavior in any way. Once a \a device created via one of the
+ QQuickGraphicsDevice factory functions, such as,
+ QQuickGraphicsDevice::fromDeviceObjects(), is passed in, and the scenegraph
+ uses a matching graphics API (with the example of fromDeviceObjects(), that
+ would be Vulkan), the scenegraph will use the existing device objects (such
+ as, the \c VkPhysicalDevice, \c VkDevice, and graphics queue family index,
+ in case of Vulkan) encapsulated by the QQuickGraphicsDevice. This allows
+ using the same device, and so sharing resources, such as buffers and
+ textures, between Qt Quick and native rendering engines.
+
+ \warning This function can only be called before initializing the
+ scenegraph and will have no effect if called afterwards. In practice this
+ typically means calling it right before QQuickRenderControl::initialize().
+
+ As an example, this time with Direct3D, the typical usage is expected to be
+ the following:
+
+ \badcode
+ // native graphics resources set up by a custom D3D rendering engine
+ ID3D11Device *device;
+ ID3D11DeviceContext *context;
+ ID3D11Texture2D *texture;
+ ...
+ // now to redirect Qt Quick content into 'texture' we could do the following:
+ QQuickRenderControl *renderControl = new QQuickRenderControl;
+ QQuickWindow *window = new QQuickWindow(renderControl); // this window will never be shown on-screen
+ ...
+ window->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(device, context));
+ renderControl->initialize();
+ window->setRenderTarget(QQuickRenderTarget::fromD3D11Texture(texture, textureSize);
+ ...
+ \endcode
+
+ The key aspect of using this function is to ensure that resources or
+ handles to resources, such as \c texture in the above example, are visible
+ to and usable by both the external rendering engine and the scenegraph
+ renderer. This requires using the same graphics device (or with OpenGL,
+ OpenGL context).
+
+ QQuickGraphicsDevice instances are implicitly shared, copyable, and
+ can be passed by value. They do not own the associated native objects (such
+ as, the ID3D11Device in the example).
+
+ \note Using QQuickRenderControl does not always imply having to call this
+ function. When adopting an existing device or context is not needed, this
+ function should not be called, and the scene graph will then initialize its
+ own devices and contexts normally, just as it would with an on-screen
+ QQuickWindow.
+
+ \since 6.0
+
+ \sa QQuickRenderControl, setRenderTarget(), setGraphicsApi()
+ */
+void QQuickWindow::setGraphicsDevice(const QQuickGraphicsDevice &device)
+{
+ Q_D(QQuickWindow);
+ d->customDeviceObjects = device;
+}
+
+/*!
+ \return the QQuickGraphicsDevice passed to setGraphicsDevice(), or a
+ default constructed one otherwise
+
+ \since 6.0
+
+ \sa setGraphicsDevice()
+ */
+QQuickGraphicsDevice QQuickWindow::graphicsDevice() const
+{
+ Q_D(const QQuickWindow);
+ return d->customDeviceObjects;
+}
+
+/*!
+ Sets the graphics configuration for this window. \a config contains various
+ settings that may be taken into account by the scene graph when
+ initializing the underlying graphics devices and contexts.
+
+ Such additional configuration, specifying for example what device
+ extensions to enable for Vulkan, becomes relevant and essential when
+ integrating native graphics rendering code that relies on certain
+ extensions. The same is true when integrating with an external 3D or VR
+ engines, such as OpenXR.
+
+ \note The configuration is ignored when adopting existing graphics devices
+ via setGraphicsDevice() since the scene graph is then not in control of the
+ actual construction of those objects.
+
+ QQuickGraphicsConfiguration instances are implicitly shared, copyable, and
+ can be passed by value.
+
+ \warning Setting a QQuickGraphicsConfiguration on a QQuickWindow must
+ happen early enough, before the scene graph is initialized for the first
+ time for that window. With on-screen windows this means the call must be
+ done before invoking show() on the QQuickWindow or QQuickView. With
+ QQuickRenderControl the configuration must be finalized before calling
+ \l{QQuickRenderControl::initialize()}{initialize()}.
+
+ \since 6.0
+ */
+void QQuickWindow::setGraphicsConfiguration(const QQuickGraphicsConfiguration &config)
+{
+ Q_D(QQuickWindow);
+ d->graphicsConfig = config;
+}
+
+/*!
+ \return the QQuickGraphicsDevice passed to setGraphicsDevice(), or a
+ default constructed one otherwise
+
+ \since 6.0
+
+ \sa setGraphicsConfiguration()
+ */
+QQuickGraphicsConfiguration QQuickWindow::graphicsConfiguration() const
+{
+ Q_D(const QQuickWindow);
+ return d->graphicsConfig;
+}
+
+/*!
+ Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
+
+ \since 5.8
+ \sa QSGRectangleNode
+ */
+QSGRectangleNode *QQuickWindow::createRectangleNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr;
+}
+
+/*!
+ Creates a simple image node. When the scenegraph is not initialized, the return value is null.
+
+ This is cross-backend alternative to constructing a QSGSimpleTextureNode directly.
+
+ \since 5.8
+ \sa QSGImageNode
+ */
+QSGImageNode *QQuickWindow::createImageNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr;
+}
+
+/*!
+ Creates a nine patch node. When the scenegraph is not initialized, the return value is null.
+
+ \since 5.8
+ */
+QSGNinePatchNode *QQuickWindow::createNinePatchNode() const
+{
+ Q_D(const QQuickWindow);
+ return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr;
+}
+
+/*!
+ \since 5.10
+
+ Returns the render type of text-like elements in Qt Quick.
+ The default is QQuickWindow::QtTextRendering.
+
+ \sa setTextRenderType()
+*/
+QQuickWindow::TextRenderType QQuickWindow::textRenderType()
+{
+ return QQuickWindowPrivate::textRenderType;
+}
+
+/*!
+ \since 5.10
+
+ Sets the default render type of text-like elements in Qt Quick to \a renderType.
+
+ \note setting the render type will only affect elements created afterwards;
+ the render type of existing elements will not be modified.
+
+ \sa textRenderType()
+*/
+void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
+{
+ QQuickWindowPrivate::textRenderType = renderType;
+}
+
+
+/*!
+ \since 6.0
+ \qmlproperty QQuickPalette Window::palette
+
+ This property holds the palette currently set for the window.
+
+ The default palette depends on the system environment. QGuiApplication maintains a system/theme
+ palette which serves as a default for all application windows. You can also set the default palette
+ for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML.
+
+ ApplicationWindow propagates explicit palette properties to child controls. If you change a specific
+ property on the window's palette, that property propagates to all child controls in the window,
+ overriding any system defaults for that property.
+
+ \sa Item::palette, Popup::palette, QQuickColorGroup
+ //! internal \sa QQuickAbstractPaletteProvider, QQuickPalette
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QQuickWindow *win)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace();
+ if (!win) {
+ debug << "QQuickWindow(0)";
+ return debug;
+ }
+
+ debug << win->metaObject()->className() << '(' << static_cast<const void *>(win);
+ if (win->isActive())
+ debug << " active";
+ if (win->isExposed())
+ debug << " exposed";
+ debug << ", visibility=" << win->visibility() << ", flags=" << win->flags();
+ if (!win->title().isEmpty())
+ debug << ", title=" << win->title();
+ if (!win->objectName().isEmpty())
+ debug << ", name=" << win->objectName();
+ if (win->parent())
+ debug << ", parent=" << static_cast<const void *>(win->parent());
+ if (win->transientParent())
+ debug << ", transientParent=" << static_cast<const void *>(win->transientParent());
+ debug << ", geometry=";
+ QtDebugUtils::formatQRect(debug, win->geometry());
+ debug << ')';
+ return debug;
+}
+#endif
+
+#include "moc_qquickwindow.cpp"
+
+QT_END_NAMESPACE