1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickwindow.h"
41#include "qquickwindow_p.h"
42
43#include "qquickitem.h"
44#include "qquickitem_p.h"
45#include "qquickevents_p_p.h"
46
47#if QT_CONFIG(quick_draganddrop)
48#include <private/qquickdrag_p.h>
49#endif
50#include <private/qquickhoverhandler_p.h>
51#include <private/qquickpointerhandler_p.h>
52
53#include <QtQuick/private/qsgrenderer_p.h>
54#include <QtQuick/private/qsgplaintexture_p.h>
55#include <private/qsgrenderloop_p.h>
56#include <private/qsgrhisupport_p.h>
57#include <private/qquickrendercontrol_p.h>
58#include <private/qquickanimatorcontroller_p.h>
59#include <private/qquickprofiler_p.h>
60
61#include <private/qguiapplication_p.h>
62#include <QtGui/QInputMethod>
63
64#include <private/qabstractanimation_p.h>
65
66#include <QtGui/qpainter.h>
67#include <QtGui/qevent.h>
68#include <QtGui/qmatrix4x4.h>
69#include <QtGui/qpa/qplatformtheme.h>
70#include <QtCore/qvarlengtharray.h>
71#include <QtCore/qabstractanimation.h>
72#include <QtCore/QLibraryInfo>
73#include <QtCore/QRunnable>
74#include <QtQml/qqmlincubator.h>
75#include <QtQml/qqmlinfo.h>
76#include <QtQml/private/qqmlmetatype_p.h>
77
78#include <QtQuick/private/qquickpixmapcache_p.h>
79
80#include <private/qqmldebugserviceinterfaces_p.h>
81#include <private/qqmldebugconnector_p.h>
82#if QT_CONFIG(opengl)
83# include <private/qopenglvertexarrayobject_p.h>
84# include <private/qsgdefaultrendercontext_p.h>
85#endif
86#ifndef QT_NO_DEBUG_STREAM
87#include <private/qdebug_p.h>
88#endif
89
90#include <QtGui/private/qrhi_p.h>
91
92QT_BEGIN_NAMESPACE
93
94Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
95Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
96Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
97Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
98Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
99Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
100Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
101Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
102Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
103Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
104Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient")
105
106extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
107extern 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);
108
109bool QQuickWindowPrivate::defaultAlphaBuffer = false;
110
111#if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE)
112QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE;
113#else
114QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering;
115#endif
116
117void QQuickWindowPrivate::updateFocusItemTransform()
118{
119#if QT_CONFIG(im)
120 Q_Q(QQuickWindow);
121 QQuickItem *focus = q->activeFocusItem();
122 if (focus && QGuiApplication::focusObject() == focus) {
123 QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(item: focus);
124 QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
125 QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
126 focus->updateInputMethod(queries: Qt::ImInputItemClipRectangle);
127 }
128#endif
129}
130
131class QQuickWindowIncubationController : public QObject, public QQmlIncubationController
132{
133 Q_OBJECT
134
135public:
136 QQuickWindowIncubationController(QSGRenderLoop *loop)
137 : m_renderLoop(loop), m_timer(0)
138 {
139 // Allow incubation for 1/3 of a frame.
140 m_incubation_time = qMax(a: 1, b: int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3);
141
142 QAnimationDriver *animationDriver = m_renderLoop->animationDriver();
143 if (animationDriver) {
144 connect(sender: animationDriver, SIGNAL(stopped()), receiver: this, SLOT(animationStopped()));
145 connect(sender: m_renderLoop, SIGNAL(timeToIncubate()), receiver: this, SLOT(incubate()));
146 }
147 }
148
149protected:
150 void timerEvent(QTimerEvent *) override
151 {
152 killTimer(id: m_timer);
153 m_timer = 0;
154 incubate();
155 }
156
157 void incubateAgain() {
158 if (m_timer == 0) {
159 // Wait for a while before processing the next batch. Using a
160 // timer to avoid starvation of system events.
161 m_timer = startTimer(interval: m_incubation_time);
162 }
163 }
164
165public slots:
166 void incubate() {
167 if (m_renderLoop && incubatingObjectCount()) {
168 if (m_renderLoop->interleaveIncubation()) {
169 incubateFor(msecs: m_incubation_time);
170 } else {
171 incubateFor(msecs: m_incubation_time * 2);
172 if (incubatingObjectCount())
173 incubateAgain();
174 }
175 }
176 }
177
178 void animationStopped() { incubate(); }
179
180protected:
181 void incubatingObjectCountChanged(int count) override
182 {
183 if (count && m_renderLoop && !m_renderLoop->interleaveIncubation())
184 incubateAgain();
185 }
186
187private:
188 QPointer<QSGRenderLoop> m_renderLoop;
189 int m_incubation_time;
190 int m_timer;
191};
192
193#include "qquickwindow.moc"
194#include "moc_qquickwindow_p.cpp"
195
196
197#if QT_CONFIG(accessibility)
198/*!
199 Returns an accessibility interface for this window, or 0 if such an
200 interface cannot be created.
201*/
202QAccessibleInterface *QQuickWindow::accessibleRoot() const
203{
204 return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this));
205}
206#endif
207
208
209/*
210Focus behavior
211==============
212
213Prior to being added to a valid window items can set and clear focus with no
214effect. Only once items are added to a window (by way of having a parent set that
215already belongs to a window) do the focus rules apply. Focus goes back to
216having no effect if an item is removed from a window.
217
218When an item is moved into a new focus scope (either being added to a window
219for the first time, or having its parent changed), if the focus scope already has
220a scope focused item that takes precedence over the item being added. Otherwise,
221the focus of the added tree is used. In the case of a tree of items being
222added to a window for the first time, which may have a conflicted focus state (two
223or more items in one scope having focus set), the same rule is applied item by item -
224thus the first item that has focus will get it (assuming the scope doesn't already
225have a scope focused item), and the other items will have their focus cleared.
226*/
227
228QQuickRootItem::QQuickRootItem()
229{
230}
231
232/*! \reimp */
233void QQuickWindow::exposeEvent(QExposeEvent *)
234{
235 Q_D(QQuickWindow);
236 if (d->windowManager)
237 d->windowManager->exposureChanged(window: this);
238}
239
240/*! \reimp */
241void QQuickWindow::resizeEvent(QResizeEvent *ev)
242{
243 Q_D(QQuickWindow);
244 if (d->contentItem)
245 d->contentItem->setSize(ev->size());
246 if (d->windowManager)
247 d->windowManager->resize(this);
248}
249
250/*! \reimp */
251void QQuickWindow::showEvent(QShowEvent *)
252{
253 Q_D(QQuickWindow);
254 if (d->windowManager)
255 d->windowManager->show(window: this);
256}
257
258/*! \reimp */
259void QQuickWindow::hideEvent(QHideEvent *)
260{
261 Q_D(QQuickWindow);
262 if (d->windowManager)
263 d->windowManager->hide(window: this);
264}
265
266/*! \reimp */
267void QQuickWindow::focusOutEvent(QFocusEvent *ev)
268{
269 Q_D(QQuickWindow);
270 if (d->contentItem)
271 d->contentItem->setFocus(focus: false, reason: ev->reason());
272}
273
274/*! \reimp */
275void QQuickWindow::focusInEvent(QFocusEvent *ev)
276{
277 Q_D(QQuickWindow);
278 if (d->contentItem)
279 d->contentItem->setFocus(focus: true, reason: ev->reason());
280 d->updateFocusItemTransform();
281}
282
283#if QT_CONFIG(im)
284static bool transformDirtyOnItemOrAncestor(const QQuickItem *item)
285{
286 while (item) {
287 if (QQuickItemPrivate::get(item)->dirtyAttributes & (
288 QQuickItemPrivate::TransformOrigin |
289 QQuickItemPrivate::Transform |
290 QQuickItemPrivate::BasicTransform |
291 QQuickItemPrivate::Position |
292 QQuickItemPrivate::Size |
293 QQuickItemPrivate::ParentChanged |
294 QQuickItemPrivate::Clip)) {
295 return true;
296 }
297 item = item->parentItem();
298 }
299 return false;
300}
301#endif
302
303/*!
304 * \internal
305
306 A "polish loop" can occur inside QQuickWindowPrivate::polishItems(). It is when an item calls
307 polish() on an(other?) item from updatePolish(). If this anomaly happens repeatedly and without
308 interruption (of a well-behaved updatePolish() that doesn't call polish()), it is a strong
309 indication that we are heading towards an infinite polish loop. A polish loop is not a bug in
310 Qt Quick - it is a bug caused by ill-behaved items put in the scene.
311
312 We can detect this sequence of polish loops easily, since the
313 QQuickWindowPrivate::itemsToPolish is basically a stack: polish() will push to it, and
314 polishItems() will pop from it.
315 Therefore if updatePolish() calls polish(), the immediate next item polishItems() processes is
316 the item that was polished by the previous call to updatePolish().
317 We therefore just need to count the number of polish loops we detected in _sequence_.
318*/
319struct PolishLoopDetector
320{
321 PolishLoopDetector(const QVector<QQuickItem*> &itemsToPolish)
322 : itemsToPolish(itemsToPolish)
323 {
324 }
325
326 /*
327 * returns true when it detected a likely infinite loop
328 * (suggests it should abort the polish loop)
329 **/
330 bool check(QQuickItem *item, int itemsRemainingBeforeUpdatePolish)
331 {
332 if (itemsToPolish.count() > itemsRemainingBeforeUpdatePolish) {
333 // Detected potential polish loop.
334 ++numPolishLoopsInSequence;
335 if (numPolishLoopsInSequence >= 1000) {
336 // Start to warn about polish loop after 1000 consecutive polish loops
337 if (numPolishLoopsInSequence == 100000) {
338 // We have looped 100,000 times without actually reducing the list of items to
339 // polish, give up for now.
340 // This is not a fix, just a remedy so that the application can be somewhat
341 // responsive.
342 numPolishLoopsInSequence = 0;
343 return true;
344 } else if (numPolishLoopsInSequence < 1005) {
345 // Show the 5 next items involved in the polish loop.
346 // (most likely they will be the same 5 items...)
347 QQuickItem *guiltyItem = itemsToPolish.last();
348 qmlWarning(me: item) << "possible QQuickItem::polish() loop";
349
350 auto typeAndObjectName = [](QQuickItem *item) {
351 QString typeName = QQmlMetaType::prettyTypeName(object: item);
352 QString objName = item->objectName();
353 if (!objName.isNull())
354 return QLatin1String("%1(%2)").arg(args&: typeName, args&: objName);
355 return typeName;
356 };
357
358 qmlWarning(me: guiltyItem) << typeAndObjectName(guiltyItem)
359 << " called polish() inside updatePolish() of " << typeAndObjectName(item);
360
361 if (numPolishLoopsInSequence == 1004)
362 // Enough warnings. Reset counter in order to speed things up and re-detect
363 // more loops
364 numPolishLoopsInSequence = 0;
365 }
366 }
367 } else {
368 numPolishLoopsInSequence = 0;
369 }
370 return false;
371 }
372 const QVector<QQuickItem*> &itemsToPolish; // Just a ref to the one in polishItems()
373 int numPolishLoopsInSequence = 0;
374};
375
376void QQuickWindowPrivate::polishItems()
377{
378 // An item can trigger polish on another item, or itself for that matter,
379 // during its updatePolish() call. Because of this, we cannot simply
380 // iterate through the set, we must continue pulling items out until it
381 // is empty.
382 // In the case where polish is called from updatePolish() either directly
383 // or indirectly, we use a PolishLoopDetector to determine if a warning should
384 // be printed to the user.
385
386 PolishLoopDetector polishLoopDetector(itemsToPolish);
387 while (!itemsToPolish.isEmpty()) {
388 QQuickItem *item = itemsToPolish.takeLast();
389 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
390 itemPrivate->polishScheduled = false;
391 const int itemsRemaining = itemsToPolish.count();
392 itemPrivate->updatePolish();
393 item->updatePolish();
394 if (polishLoopDetector.check(item, itemsRemainingBeforeUpdatePolish: itemsRemaining) == true)
395 break;
396 }
397
398#if QT_CONFIG(im)
399 if (QQuickItem *focusItem = q_func()->activeFocusItem()) {
400 // If the current focus item, or any of its anchestors, has changed location
401 // inside the window, we need inform IM about it. This to ensure that overlays
402 // such as selection handles will be updated.
403 const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject());
404 const bool hasImEnabled = focusItem->inputMethodQuery(query: Qt::ImEnabled).toBool();
405 if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(item: focusItem))
406 updateFocusItemTransform();
407 }
408#endif
409}
410
411/*!
412 * Schedules the window to render another frame.
413 *
414 * Calling QQuickWindow::update() differs from QQuickItem::update() in that
415 * it always triggers a repaint, regardless of changes in the underlying
416 * scene graph or not.
417 */
418void QQuickWindow::update()
419{
420 Q_D(QQuickWindow);
421 if (d->windowManager)
422 d->windowManager->update(window: this);
423 else if (d->renderControl)
424 QQuickRenderControlPrivate::get(renderControl: d->renderControl)->update();
425}
426
427static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio)
428{
429 if (item->flags() & QQuickItem::ItemHasContents) {
430 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
431 itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio);
432 }
433
434 QList <QQuickItem *> items = item->childItems();
435 for (int i = 0; i < items.size(); ++i)
436 updatePixelRatioHelper(item: items.at(i), pixelRatio);
437}
438
439void QQuickWindow::physicalDpiChanged()
440{
441 Q_D(QQuickWindow);
442 const qreal newPixelRatio = screen()->devicePixelRatio();
443 if (qFuzzyCompare(p1: newPixelRatio, p2: d->devicePixelRatio))
444 return;
445 d->devicePixelRatio = newPixelRatio;
446 if (d->contentItem)
447 updatePixelRatioHelper(item: d->contentItem, pixelRatio: newPixelRatio);
448}
449
450void QQuickWindow::handleScreenChanged(QScreen *screen)
451{
452 Q_D(QQuickWindow);
453 // we connected to the initial screen in QQuickWindowPrivate::init, but the screen changed
454 disconnect(d->physicalDpiChangedConnection);
455 if (screen) {
456 physicalDpiChanged();
457 // When physical DPI changes on the same screen, either the resolution or the device pixel
458 // ratio changed. We must check what it is. Device pixel ratio does not have its own
459 // ...Changed() signal. Reconnect, same as in QQuickWindowPrivate::init.
460 d->physicalDpiChangedConnection = connect(sender: screen, signal: &QScreen::physicalDotsPerInchChanged,
461 receiver: this, slot: &QQuickWindow::physicalDpiChanged);
462 }
463
464 d->forcePolish();
465}
466
467void forcePolishHelper(QQuickItem *item)
468{
469 if (item->flags() & QQuickItem::ItemHasContents) {
470 item->polish();
471 }
472
473 QList <QQuickItem *> items = item->childItems();
474 for (int i=0; i<items.size(); ++i)
475 forcePolishHelper(item: items.at(i));
476}
477
478/*!
479 Schedules polish events on all items in the scene.
480*/
481void QQuickWindowPrivate::forcePolish()
482{
483 Q_Q(QQuickWindow);
484 if (!q->screen())
485 return;
486 forcePolishHelper(item: contentItem);
487}
488
489void forceUpdate(QQuickItem *item)
490{
491 if (item->flags() & QQuickItem::ItemHasContents)
492 item->update();
493 QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask);
494
495 QList <QQuickItem *> items = item->childItems();
496 for (int i=0; i<items.size(); ++i)
497 forceUpdate(item: items.at(i));
498}
499
500void QQuickWindowPrivate::syncSceneGraph()
501{
502 Q_Q(QQuickWindow);
503
504 // Calculate the dpr the same way renderSceneGraph() will.
505 qreal devicePixelRatio = q->effectiveDevicePixelRatio();
506 if (renderTargetId && !QQuickRenderControl::renderWindowFor(win: q))
507 devicePixelRatio = 1;
508
509 context->prepareSync(devicePixelRatio, cb: rhi ? swapchain->currentFrameCommandBuffer() : nullptr);
510
511 animationController->beforeNodeSync();
512
513 emit q->beforeSynchronizing();
514 runAndClearJobs(jobs: &beforeSynchronizingJobs);
515 if (!renderer) {
516 forceUpdate(item: contentItem);
517
518 QSGRootNode *rootNode = new QSGRootNode;
519 rootNode->appendChildNode(node: QQuickItemPrivate::get(item: contentItem)->itemNode());
520 renderer = context->createRenderer();
521 renderer->setRootNode(rootNode);
522 }
523
524 updateDirtyNodes();
525
526 animationController->afterNodeSync();
527
528 // Copy the current state of clearing from window into renderer.
529 renderer->setClearColor(clearColor);
530 QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearStencilBuffer | QSGAbstractRenderer::ClearDepthBuffer;
531 if (clearBeforeRendering)
532 mode |= QSGAbstractRenderer::ClearColorBuffer;
533 renderer->setClearMode(mode);
534
535 renderer->setCustomRenderMode(customRenderMode);
536
537 emit q->afterSynchronizing();
538 runAndClearJobs(jobs: &afterSynchronizingJobs);
539}
540
541void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud)
542{
543 QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
544 emit w->beforeRenderPassRecording();
545}
546
547void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud)
548{
549 QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud);
550 emit w->afterRenderPassRecording();
551}
552
553void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfaceSize)
554{
555 Q_Q(QQuickWindow);
556 if (!renderer)
557 return;
558
559 if (rhi) {
560 // ### no offscreen ("renderTargetId") support yet
561 context->beginNextRhiFrame(renderer,
562 rt: swapchain->currentFrameRenderTarget(),
563 rp: rpDescForSwapchain,
564 cb: swapchain->currentFrameCommandBuffer(),
565 mainPassRecordingStart: emitBeforeRenderPassRecording,
566 mainPassRecordingEnd: emitAfterRenderPassRecording,
567 callbackUserData: q);
568 } else {
569 context->beginNextFrame(renderer,
570 mainPassRecordingStart: emitBeforeRenderPassRecording,
571 mainPassRecordingEnd: emitAfterRenderPassRecording,
572 callbackUserData: q);
573 }
574
575 animationController->advance();
576 emit q->beforeRendering();
577 runAndClearJobs(jobs: &beforeRenderingJobs);
578 if (!customRenderStage || !customRenderStage->render()) {
579 int fboId = 0;
580 const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
581 if (renderTargetId) {
582 QRect rect(QPoint(0, 0), renderTargetSize);
583 fboId = renderTargetId;
584 renderer->setDeviceRect(rect);
585 renderer->setViewportRect(rect);
586 if (QQuickRenderControl::renderWindowFor(win: q)) {
587 renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size));
588 renderer->setDevicePixelRatio(devicePixelRatio);
589 } else {
590 renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()));
591 renderer->setDevicePixelRatio(1);
592 }
593 } else {
594 QSize pixelSize;
595 QSizeF logicalSize;
596 if (surfaceSize.isEmpty()) {
597 pixelSize = size * devicePixelRatio;
598 logicalSize = size;
599 } else {
600 pixelSize = surfaceSize;
601 logicalSize = QSizeF(surfaceSize) / devicePixelRatio;
602 }
603 QRect rect(QPoint(0, 0), pixelSize);
604 renderer->setDeviceRect(rect);
605 renderer->setViewportRect(rect);
606 const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
607 QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
608 if (flipY)
609 matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
610 renderer->setProjectionMatrixToRect(rect: QRectF(QPoint(0, 0), logicalSize), flags: matrixFlags);
611 renderer->setDevicePixelRatio(devicePixelRatio);
612 }
613
614 if (rhi)
615 context->renderNextRhiFrame(renderer);
616 else
617 context->renderNextFrame(renderer, fboId);
618 }
619 emit q->afterRendering();
620 runAndClearJobs(jobs: &afterRenderingJobs);
621
622 if (rhi)
623 context->endNextRhiFrame(renderer);
624 else
625 context->endNextFrame(renderer);
626
627 if (renderer && renderer->hasCustomRenderModeWithContinuousUpdate()) {
628 // For the overdraw visualizer. This update is not urgent so avoid a
629 // direct update() call, this is only here to keep the overdraw
630 // visualization box rotating even when the scene is static.
631 QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent::Type(FullUpdateRequest)));
632 }
633}
634
635QQuickWindowPrivate::QQuickWindowPrivate()
636 : contentItem(nullptr)
637 , activeFocusItem(nullptr)
638#if QT_CONFIG(cursor)
639 , cursorItem(nullptr)
640 , cursorHandler(nullptr)
641#endif
642#if QT_CONFIG(quick_draganddrop)
643 , dragGrabber(nullptr)
644#endif
645 , touchMouseId(-1)
646 , touchMouseDevice(nullptr)
647 , touchMousePressTimestamp(0)
648 , dirtyItemList(nullptr)
649 , devicePixelRatio(0)
650 , context(nullptr)
651 , renderer(nullptr)
652 , windowManager(nullptr)
653 , renderControl(nullptr)
654 , pointerEventRecursionGuard(0)
655 , customRenderStage(nullptr)
656 , clearColor(Qt::white)
657 , clearBeforeRendering(true)
658 , persistentGLContext(true)
659 , persistentSceneGraph(true)
660 , lastWheelEventAccepted(false)
661 , componentCompleted(true)
662 , allowChildEventFiltering(true)
663 , allowDoubleClick(true)
664 , lastFocusReason(Qt::OtherFocusReason)
665 , renderTarget(nullptr)
666 , renderTargetId(0)
667 , vaoHelper(nullptr)
668 , incubationController(nullptr)
669 , hasActiveSwapchain(false)
670 , hasRenderableSwapchain(false)
671 , swapchainJustBecameRenderable(false)
672{
673#if QT_CONFIG(quick_draganddrop)
674 dragGrabber = new QQuickDragGrabber;
675#endif
676}
677
678QQuickWindowPrivate::~QQuickWindowPrivate()
679{
680 delete customRenderStage;
681 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
682 service->removeWindow(q_func());
683}
684
685void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
686{
687 q_ptr = c;
688
689
690 Q_Q(QQuickWindow);
691
692 contentItem = new QQuickRootItem;
693 QQml_setParent_noEvent(object: contentItem, parent: c);
694 QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
695 QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(item: contentItem);
696 contentItemPrivate->window = q;
697 contentItemPrivate->windowRefCount = 1;
698 contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
699 contentItem->setSize(q->size());
700
701 customRenderMode = qgetenv(varName: "QSG_VISUALIZE");
702 renderControl = control;
703 if (renderControl)
704 QQuickRenderControlPrivate::get(renderControl)->window = q;
705
706 if (!renderControl)
707 windowManager = QSGRenderLoop::instance();
708
709 Q_ASSERT(windowManager || renderControl);
710
711 if (QScreen *screen = q->screen()) {
712 devicePixelRatio = screen->devicePixelRatio();
713 // if the screen changes, then QQuickWindow::handleScreenChanged disconnects
714 // and connects to the new screen
715 physicalDpiChangedConnection = QObject::connect(sender: screen, signal: &QScreen::physicalDotsPerInchChanged,
716 receiver: q, slot: &QQuickWindow::physicalDpiChanged);
717 }
718
719 QSGContext *sg;
720 if (renderControl) {
721 QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl);
722 sg = renderControlPriv->sg;
723 context = renderControlPriv->rc;
724 } else {
725 windowManager->addWindow(win: q);
726 sg = windowManager->sceneGraphContext();
727 context = windowManager->createRenderContext(sg);
728 }
729
730 q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
731 q->setFormat(sg->defaultSurfaceFormat());
732#if QT_CONFIG(vulkan)
733 if (q->surfaceType() == QSurface::VulkanSurface)
734 q->setVulkanInstance(QSGRhiSupport::vulkanInstance());
735#endif
736
737 animationController.reset(other: new QQuickAnimatorController(q));
738
739 QObject::connect(sender: context, SIGNAL(initialized()), receiver: q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection);
740 QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection);
741 QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SLOT(cleanupSceneGraph()), Qt::DirectConnection);
742
743 QObject::connect(sender: q, SIGNAL(focusObjectChanged(QObject*)), receiver: q, SIGNAL(activeFocusItemChanged()));
744 QObject::connect(sender: q, SIGNAL(screenChanged(QScreen*)), receiver: q, SLOT(handleScreenChanged(QScreen*)));
745 QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
746 receiver: q, SLOT(handleApplicationStateChanged(Qt::ApplicationState)));
747 QObject::connect(sender: q, SIGNAL(frameSwapped()), receiver: q, SLOT(runJobsAfterSwap()), Qt::DirectConnection);
748
749 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
750 service->addWindow(q);
751}
752
753void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state)
754{
755 Q_D(QQuickWindow);
756 if (state != Qt::ApplicationActive && d->contentItem)
757 d->contentItem->windowDeactivateEvent();
758}
759
760/*!
761 \property QQuickWindow::data
762 \internal
763*/
764
765QQmlListProperty<QObject> QQuickWindowPrivate::data()
766{
767 return QQmlListProperty<QObject>(q_func(), nullptr,
768 QQuickWindowPrivate::data_append,
769 QQuickWindowPrivate::data_count,
770 QQuickWindowPrivate::data_at,
771 QQuickWindowPrivate::data_clear,
772 QQuickWindowPrivate::data_replace,
773 QQuickWindowPrivate::data_removeLast);
774}
775
776static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
777{
778 Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
779 // The touch point local position and velocity are not yet transformed.
780 QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(point: p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
781 Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers());
782 me->setAccepted(true);
783 me->setTimestamp(event->timestamp());
784 QVector2D transformedVelocity = p.velocity();
785 if (transformNeeded) {
786 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
787 QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
788 transformedVelocity = transformMatrix.mapVector(vector: p.velocity()).toVector2D();
789 }
790 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: me, caps: event->device()->capabilities(), velocity: transformedVelocity);
791 QGuiApplicationPrivate::setMouseEventSource(event: me, source: Qt::MouseEventSynthesizedByQt);
792 return me;
793}
794
795bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
796{
797 bool doubleClicked = false;
798
799 if (touchMousePressTimestamp > 0) {
800 QPoint distanceBetweenPresses = newPressPos - touchMousePressPos;
801 const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance();
802 doubleClicked = (qAbs(t: distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(t: distanceBetweenPresses.y()) <= doubleTapDistance);
803
804 if (doubleClicked) {
805 ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp;
806 ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->
807 mouseDoubleClickInterval());
808 doubleClicked = timeBetweenPresses < doubleClickInterval;
809 }
810 }
811 if (doubleClicked) {
812 touchMousePressTimestamp = 0;
813 } else {
814 touchMousePressTimestamp = newPressEventTimestamp;
815 touchMousePressPos = newPressPos;
816 }
817
818 return doubleClicked;
819}
820
821void QQuickWindowPrivate::cancelTouchMouseSynthesis()
822{
823 qCDebug(DBG_TOUCH_TARGET);
824 touchMouseId = -1;
825 touchMouseDevice = nullptr;
826}
827
828bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent)
829{
830 Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
831 Q_Q(QQuickWindow);
832 auto device = pointerEvent->device();
833
834 // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant
835 if (device->type() == QQuickPointerDevice::TouchPad && device->capabilities().testFlag(flag: QQuickPointerDevice::MouseEmulation)) {
836 qCDebug(DBG_TOUCH_TARGET) << "skipping delivery of synth-mouse event from" << device;
837 return false;
838 }
839
840 // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
841 Q_ASSERT(pointerEvent->asPointerTouchEvent());
842 QScopedPointer<QTouchEvent> event(pointerEvent->asPointerTouchEvent()->touchEventForItem(item));
843 if (event.isNull())
844 return false;
845
846 // For each point, check if it is accepted, if not, try the next point.
847 // Any of the fingers can become the mouse one.
848 // This can happen because a mouse area might not accept an event at some point but another.
849 for (int i = 0; i < event->touchPoints().count(); ++i) {
850 const QTouchEvent::TouchPoint &p = event->touchPoints().at(i);
851 // A new touch point
852 if (touchMouseId == -1 && p.state() & Qt::TouchPointPressed) {
853 QPointF pos = item->mapFromScene(point: p.scenePos());
854
855 // probably redundant, we check bounds in the calling function (matchingNewPoints)
856 if (!item->contains(point: pos))
857 break;
858
859 qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
860 QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(type: QEvent::MouseButtonPress, p, event: event.data(), item, transformNeeded: false));
861
862 // Send a single press and see if that's accepted
863 QCoreApplication::sendEvent(receiver: item, event: mousePress.data());
864 event->setAccepted(mousePress->isAccepted());
865 if (mousePress->isAccepted()) {
866 touchMouseDevice = device;
867 touchMouseId = p.id();
868 if (!q->mouseGrabberItem())
869 item->grabMouse();
870 if (auto pointerEventPoint = pointerEvent->pointById(pointId: p.id()))
871 pointerEventPoint->setGrabberItem(item);
872
873 if (checkIfDoubleTapped(newPressEventTimestamp: event->timestamp(), newPressPos: p.screenPos().toPoint())) {
874 // since we synth the mouse event from from touch, we respect the
875 // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance
876 QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(type: QEvent::MouseButtonDblClick, p, event: event.data(), item, transformNeeded: false));
877 QCoreApplication::sendEvent(receiver: item, event: mouseDoubleClick.data());
878 event->setAccepted(mouseDoubleClick->isAccepted());
879 if (!mouseDoubleClick->isAccepted())
880 cancelTouchMouseSynthesis();
881 }
882
883 return true;
884 }
885 // try the next point
886
887 // Touch point was there before and moved
888 } else if (touchMouseDevice == device && p.id() == touchMouseId) {
889 if (p.state() & Qt::TouchPointMoved) {
890 if (touchMousePressTimestamp != 0) {
891 const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(hint: QPlatformTheme::TouchDoubleTapDistance).toInt();
892 const QPoint moveDelta = p.screenPos().toPoint() - touchMousePressPos;
893 if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance)
894 touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap
895 }
896 if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
897 QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseMove, p, event: event.data(), item: mouseGrabberItem, transformNeeded: false));
898 QCoreApplication::sendEvent(receiver: item, event: me.data());
899 event->setAccepted(me->isAccepted());
900 if (me->isAccepted()) {
901 qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
902 }
903 return event->isAccepted();
904 } else {
905 // no grabber, check if we care about mouse hover
906 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
907 // hover for touch???
908 QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseMove, p, event: event.data(), item, transformNeeded: false));
909 if (lastMousePosition.isNull())
910 lastMousePosition = me->windowPos();
911 QPointF last = lastMousePosition;
912 lastMousePosition = me->windowPos();
913
914 bool accepted = me->isAccepted();
915 bool delivered = deliverHoverEvent(contentItem, scenePos: me->windowPos(), lastScenePos: last, modifiers: me->modifiers(), timestamp: me->timestamp(), accepted);
916 if (!delivered) {
917 //take care of any exits
918 accepted = clearHover(timestamp: me->timestamp());
919 }
920 me->setAccepted(accepted);
921 break;
922 }
923 } else if (p.state() & Qt::TouchPointReleased) {
924 // currently handled point was released
925 if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) {
926 QScopedPointer<QMouseEvent> me(touchToMouseEvent(type: QEvent::MouseButtonRelease, p, event: event.data(), item: mouseGrabberItem, transformNeeded: false));
927 QCoreApplication::sendEvent(receiver: item, event: me.data());
928
929 if (item->acceptHoverEvents() && p.screenPos() != QGuiApplicationPrivate::lastCursorPosition) {
930 QPointF localMousePos(qInf(), qInf());
931 if (QWindow *w = item->window())
932 localMousePos = item->mapFromScene(point: w->mapFromGlobal(pos: QGuiApplicationPrivate::lastCursorPosition.toPoint()));
933 QMouseEvent mm(QEvent::MouseMove, localMousePos, QGuiApplicationPrivate::lastCursorPosition,
934 Qt::NoButton, Qt::NoButton, event->modifiers());
935 QCoreApplication::sendEvent(receiver: item, event: &mm);
936 }
937 if (q->mouseGrabberItem()) // might have ungrabbed due to event
938 q->mouseGrabberItem()->ungrabMouse();
939
940 cancelTouchMouseSynthesis();
941 return me->isAccepted();
942 }
943 }
944 break;
945 }
946 }
947 return false;
948}
949
950void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids)
951{
952 QQuickPointerEvent *ev = nullptr;
953 for (int i = 0; i < ids.count(); ++i) {
954 int id = ids.at(i);
955 if (Q_UNLIKELY(id < 0)) {
956 qWarning(msg: "ignoring grab of touchpoint %d", id);
957 continue;
958 }
959 if (id == touchMouseId) {
960 auto point = pointerEventInstance(device: touchMouseDevice)->pointById(pointId: id);
961 auto touchMouseGrabber = point->grabberItem();
962 if (touchMouseGrabber) {
963 point->setExclusiveGrabber(nullptr);
964 touchMouseGrabber->mouseUngrabEvent();
965 touchMouseGrabber->touchUngrabEvent();
966 cancelTouchMouseSynthesis();
967 }
968 qCDebug(DBG_MOUSE_TARGET) << "grabTouchPoints: mouse grabber changed due to grabTouchPoints:" << touchMouseGrabber << "-> null";
969 }
970
971 // optimization to avoid the loop over devices below:
972 // all ids are probably from the same event, so we don't have to search
973 if (ev) {
974 auto point = ev->pointById(pointId: id);
975 if (point && point->exclusiveGrabber() != grabber) {
976 point->setExclusiveGrabber(grabber);
977 continue; // next id in the ids loop
978 }
979 }
980 // search all devices for a QQuickPointerEvent instance that is delivering the point with id
981 const auto touchDevices = QQuickPointerDevice::touchDevices();
982 for (auto device : touchDevices) {
983 QQuickPointerEvent *pev = pointerEventInstance(device);
984 auto point = pev->pointById(pointId: id);
985 if (point) {
986 ev = pev;
987 if (point->exclusiveGrabber() != grabber)
988 point->setExclusiveGrabber(grabber);
989 break; // out of touchDevices loop
990 }
991 }
992 }
993}
994
995/*!
996 Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber.
997 This should not be called when processing a release event - that's redundant.
998 It is called in other cases, when the points may not be released, but the item
999 nevertheless must lose its grab due to becoming disabled, invisible, etc.
1000 QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released,
1001 but if not all points are released, it cannot be sure whether to call touchUngrabEvent()
1002 or not; so we have to do it here.
1003*/
1004void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch)
1005{
1006 Q_Q(QQuickWindow);
1007 if (Q_LIKELY(mouse) && q->mouseGrabberItem() == grabber) {
1008 bool fromTouch = isDeliveringTouchAsMouse();
1009 auto point = fromTouch ?
1010 pointerEventInstance(device: touchMouseDevice)->pointById(pointId: touchMouseId) :
1011 pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())->point(i: 0);
1012 QQuickItem *oldGrabber = point->grabberItem();
1013 qCDebug(DBG_MOUSE_TARGET) << "removeGrabber" << oldGrabber << "-> null";
1014 point->setGrabberItem(nullptr);
1015 sendUngrabEvent(grabber: oldGrabber, touch: fromTouch);
1016 }
1017 if (Q_LIKELY(touch)) {
1018 bool ungrab = false;
1019 const auto touchDevices = QQuickPointerDevice::touchDevices();
1020 for (auto device : touchDevices) {
1021 if (auto pointerEvent = queryPointerEventInstance(device)) {
1022 for (int i = 0; i < pointerEvent->pointCount(); ++i) {
1023 if (pointerEvent->point(i)->exclusiveGrabber() == grabber) {
1024 pointerEvent->point(i)->setGrabberItem(nullptr);
1025 ungrab = true;
1026 }
1027 }
1028 }
1029 }
1030 if (ungrab)
1031 grabber->touchUngrabEvent();
1032 }
1033}
1034
1035void QQuickWindowPrivate::sendUngrabEvent(QQuickItem *grabber, bool touch)
1036{
1037 if (!grabber)
1038 return;
1039 QEvent e(QEvent::UngrabMouse);
1040 hasFiltered.clear();
1041 if (!sendFilteredMouseEvent(event: &e, receiver: grabber, filteringParent: grabber->parentItem())) {
1042 grabber->mouseUngrabEvent();
1043 if (touch)
1044 grabber->touchUngrabEvent();
1045 }
1046}
1047
1048/*!
1049Translates the data in \a touchEvent to this window. This method leaves the item local positions in
1050\a touchEvent untouched (these are filled in later).
1051*/
1052void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent)
1053{
1054 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
1055 for (int i = 0; i < touchPoints.count(); ++i) {
1056 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1057 touchPoint.setScenePos(touchPoint.pos());
1058 touchPoint.setStartScenePos(touchPoint.startPos());
1059 touchPoint.setLastScenePos(touchPoint.lastPos());
1060 }
1061 touchEvent->setTouchPoints(touchPoints);
1062}
1063
1064
1065static inline bool windowHasFocus(QQuickWindow *win)
1066{
1067 const QWindow *focusWindow = QGuiApplication::focusWindow();
1068 return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow;
1069}
1070
1071#ifdef Q_OS_WEBOS
1072// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
1073static inline bool singleWindowOnScreen(QQuickWindow *win)
1074{
1075 const QWindowList windowList = QGuiApplication::allWindows();
1076 for (int i = 0; i < windowList.count(); i++) {
1077 QWindow *ii = windowList.at(i);
1078 if (ii == win)
1079 continue;
1080 if (ii->screen() == win->screen())
1081 return false;
1082 }
1083
1084 return true;
1085}
1086#endif
1087
1088/*!
1089Set the focus inside \a scope to be \a item.
1090If the scope contains the active focus item, it will be changed to \a item.
1091Calls notifyFocusChangesRecur for all changed items.
1092*/
1093void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
1094{
1095 Q_Q(QQuickWindow);
1096
1097 Q_ASSERT(item);
1098 Q_ASSERT(scope || item == contentItem);
1099
1100 qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::setFocusInScope():";
1101 qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope;
1102 if (scope)
1103 qCDebug(DBG_FOCUS) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(item: scope)->subFocusItem;
1104 qCDebug(DBG_FOCUS) << " item:" << (QObject *)item;
1105 qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem;
1106
1107 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(item: scope) : nullptr;
1108 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1109
1110 QQuickItem *oldActiveFocusItem = nullptr;
1111 QQuickItem *currentActiveFocusItem = activeFocusItem;
1112 QQuickItem *newActiveFocusItem = nullptr;
1113 bool sendFocusIn = false;
1114
1115 lastFocusReason = reason;
1116
1117 QVarLengthArray<QQuickItem *, 20> changed;
1118
1119 // Does this change the active focus?
1120 if (item == contentItem || scopePrivate->activeFocus) {
1121 oldActiveFocusItem = activeFocusItem;
1122 if (item->isEnabled()) {
1123 newActiveFocusItem = item;
1124 while (newActiveFocusItem->isFocusScope()
1125 && newActiveFocusItem->scopedFocusItem()
1126 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
1127 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
1128 }
1129 } else {
1130 newActiveFocusItem = scope;
1131 }
1132
1133 if (oldActiveFocusItem) {
1134#if QT_CONFIG(im)
1135 QGuiApplication::inputMethod()->commit();
1136#endif
1137
1138 activeFocusItem = nullptr;
1139
1140 QQuickItem *afi = oldActiveFocusItem;
1141 while (afi && afi != scope) {
1142 if (QQuickItemPrivate::get(item: afi)->activeFocus) {
1143 QQuickItemPrivate::get(item: afi)->activeFocus = false;
1144 changed << afi;
1145 }
1146 afi = afi->parentItem();
1147 }
1148 }
1149 }
1150
1151 if (item != contentItem && !(options & DontChangeSubFocusItem)) {
1152 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
1153 if (oldSubFocusItem) {
1154 QQuickItemPrivate::get(item: oldSubFocusItem)->focus = false;
1155 changed << oldSubFocusItem;
1156 }
1157
1158 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, focus: true);
1159 }
1160
1161 if (!(options & DontChangeFocusProperty)) {
1162 if (item != contentItem
1163 || windowHasFocus(win: q)
1164#ifdef Q_OS_WEBOS
1165 // Allow focused if there is only one window in the screen where it belongs.
1166 // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
1167 || singleWindowOnScreen(q)
1168#endif
1169 ) {
1170 itemPrivate->focus = true;
1171 changed << item;
1172 }
1173 }
1174
1175 if (newActiveFocusItem && contentItem->hasFocus()) {
1176 activeFocusItem = newActiveFocusItem;
1177
1178 QQuickItemPrivate::get(item: newActiveFocusItem)->activeFocus = true;
1179 changed << newActiveFocusItem;
1180
1181 QQuickItem *afi = newActiveFocusItem->parentItem();
1182 while (afi && afi != scope) {
1183 if (afi->isFocusScope()) {
1184 QQuickItemPrivate::get(item: afi)->activeFocus = true;
1185 changed << afi;
1186 }
1187 afi = afi->parentItem();
1188 }
1189 updateFocusItemTransform();
1190 sendFocusIn = true;
1191 }
1192
1193 // Now that all the state is changed, emit signals & events
1194 // We must do this last, as this process may result in further changes to focus.
1195 if (oldActiveFocusItem) {
1196 QFocusEvent event(QEvent::FocusOut, reason);
1197 QCoreApplication::sendEvent(receiver: oldActiveFocusItem, event: &event);
1198 }
1199
1200 // Make sure that the FocusOut didn't result in another focus change.
1201 if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
1202 QFocusEvent event(QEvent::FocusIn, reason);
1203 QCoreApplication::sendEvent(receiver: newActiveFocusItem, event: &event);
1204 }
1205
1206 if (activeFocusItem != currentActiveFocusItem)
1207 emit q->focusObjectChanged(object: activeFocusItem);
1208
1209 if (!changed.isEmpty())
1210 notifyFocusChangesRecur(item: changed.data(), remaining: changed.count() - 1);
1211}
1212
1213void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options)
1214{
1215 Q_Q(QQuickWindow);
1216
1217 Q_ASSERT(item);
1218 Q_ASSERT(scope || item == contentItem);
1219
1220 qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::clearFocusInScope():";
1221 qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope;
1222 qCDebug(DBG_FOCUS) << " item:" << (QObject *)item;
1223 qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem;
1224
1225 QQuickItemPrivate *scopePrivate = nullptr;
1226 if (scope) {
1227 scopePrivate = QQuickItemPrivate::get(item: scope);
1228 if ( !scopePrivate->subFocusItem )
1229 return;//No focus, nothing to do.
1230 }
1231
1232 QQuickItem *currentActiveFocusItem = activeFocusItem;
1233 QQuickItem *oldActiveFocusItem = nullptr;
1234 QQuickItem *newActiveFocusItem = nullptr;
1235
1236 lastFocusReason = reason;
1237
1238 QVarLengthArray<QQuickItem *, 20> changed;
1239
1240 Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem);
1241
1242 // Does this change the active focus?
1243 if (item == contentItem || scopePrivate->activeFocus) {
1244 oldActiveFocusItem = activeFocusItem;
1245 newActiveFocusItem = scope;
1246
1247#if QT_CONFIG(im)
1248 QGuiApplication::inputMethod()->commit();
1249#endif
1250
1251 activeFocusItem = nullptr;
1252
1253 if (oldActiveFocusItem) {
1254 QQuickItem *afi = oldActiveFocusItem;
1255 while (afi && afi != scope) {
1256 if (QQuickItemPrivate::get(item: afi)->activeFocus) {
1257 QQuickItemPrivate::get(item: afi)->activeFocus = false;
1258 changed << afi;
1259 }
1260 afi = afi->parentItem();
1261 }
1262 }
1263 }
1264
1265 if (item != contentItem && !(options & DontChangeSubFocusItem)) {
1266 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
1267 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
1268 QQuickItemPrivate::get(item: oldSubFocusItem)->focus = false;
1269 changed << oldSubFocusItem;
1270 }
1271
1272 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, focus: false);
1273
1274 } else if (!(options & DontChangeFocusProperty)) {
1275 QQuickItemPrivate::get(item)->focus = false;
1276 changed << item;
1277 }
1278
1279 if (newActiveFocusItem) {
1280 Q_ASSERT(newActiveFocusItem == scope);
1281 activeFocusItem = scope;
1282 updateFocusItemTransform();
1283 }
1284
1285 // Now that all the state is changed, emit signals & events
1286 // We must do this last, as this process may result in further changes to
1287 // focus.
1288 if (oldActiveFocusItem) {
1289 QFocusEvent event(QEvent::FocusOut, reason);
1290 QCoreApplication::sendEvent(receiver: oldActiveFocusItem, event: &event);
1291 }
1292
1293 // Make sure that the FocusOut didn't result in another focus change.
1294 if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
1295 QFocusEvent event(QEvent::FocusIn, reason);
1296 QCoreApplication::sendEvent(receiver: newActiveFocusItem, event: &event);
1297 }
1298
1299 if (activeFocusItem != currentActiveFocusItem)
1300 emit q->focusObjectChanged(object: activeFocusItem);
1301
1302 if (!changed.isEmpty())
1303 notifyFocusChangesRecur(item: changed.data(), remaining: changed.count() - 1);
1304}
1305
1306void QQuickWindowPrivate::clearFocusObject()
1307{
1308 if (activeFocusItem == contentItem)
1309 return;
1310
1311 clearFocusInScope(scope: contentItem, item: QQuickItemPrivate::get(item: contentItem)->subFocusItem, reason: Qt::OtherFocusReason);
1312}
1313
1314void QQuickWindowPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
1315{
1316 QPointer<QQuickItem> item(*items);
1317
1318 if (remaining)
1319 notifyFocusChangesRecur(items: items + 1, remaining: remaining - 1);
1320
1321 if (item) {
1322 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1323
1324 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
1325 itemPrivate->notifiedFocus = itemPrivate->focus;
1326 emit item->focusChanged(itemPrivate->focus);
1327 }
1328
1329 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
1330 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
1331 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
1332 emit item->activeFocusChanged(itemPrivate->activeFocus);
1333 }
1334 }
1335}
1336
1337void QQuickWindowPrivate::dirtyItem(QQuickItem *)
1338{
1339 Q_Q(QQuickWindow);
1340 q->maybeUpdate();
1341}
1342
1343void QQuickWindowPrivate::cleanup(QSGNode *n)
1344{
1345 Q_Q(QQuickWindow);
1346
1347 Q_ASSERT(!cleanupNodeList.contains(n));
1348 cleanupNodeList.append(t: n);
1349 q->maybeUpdate();
1350}
1351
1352/*!
1353 \qmltype Window
1354 \instantiates QQuickWindow
1355 \inqmlmodule QtQuick.Window
1356 \ingroup qtquick-visual
1357 \brief Creates a new top-level window.
1358
1359 The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the
1360 window for use with \c {QtQuick 2.x} graphical types.
1361
1362 To use this type, you will need to import the module with the following line:
1363 \code
1364 import QtQuick.Window 2.2
1365 \endcode
1366
1367 Omitting this import will allow you to have a QML environment without
1368 access to window system features.
1369
1370 A Window can be declared inside an Item or inside another Window; in that
1371 case the inner Window will automatically become "transient for" the outer
1372 Window: that is, most platforms will show it centered upon the outer window
1373 by default, and there may be other platform-dependent behaviors, depending
1374 also on the \l flags. If the nested window is intended to be a dialog in
1375 your application, you should also set \l flags to Qt.Dialog, because some
1376 window managers will not provide the centering behavior without that flag.
1377 You can also declare multiple windows inside a top-level \l QtObject, in which
1378 case the windows will have no transient relationship.
1379
1380 Alternatively you can set or bind \l x and \l y to position the Window
1381 explicitly on the screen.
1382
1383 When the user attempts to close a window, the \l closing signal will be
1384 emitted. You can force the window to stay open (for example to prompt the
1385 user to save changes) by writing an \c onClosing handler and setting
1386 \c {close.accepted = false}.
1387*/
1388/*!
1389 \class QQuickWindow
1390 \since 5.0
1391
1392 \inmodule QtQuick
1393
1394 \brief The QQuickWindow class provides the window for displaying a graphical QML scene.
1395
1396 QQuickWindow provides the graphical scene management needed to interact with and display
1397 a scene of QQuickItems.
1398
1399 A QQuickWindow always has a single invisible root item. To add items to this window,
1400 reparent the items to the root item or to an existing item in the scene.
1401
1402 For easily displaying a scene from a QML file, see \l{QQuickView}.
1403
1404 \section1 Rendering
1405
1406 QQuickWindow uses a scene graph to represent what needs to be rendered.
1407 This scene graph is disconnected from the QML scene and
1408 potentially lives in another thread, depending on the platform
1409 implementation. Since the rendering scene graph lives
1410 independently from the QML scene, it can also be completely
1411 released without affecting the state of the QML scene.
1412
1413 The sceneGraphInitialized() signal is emitted on the rendering
1414 thread before the QML scene is rendered to the screen for the
1415 first time. If the rendering scene graph has been released, the
1416 signal will be emitted again before the next frame is rendered.
1417
1418
1419 \section2 Integration with OpenGL
1420
1421 When using the default OpenGL adaptation, it is possible to integrate
1422 OpenGL calls directly into the QQuickWindow using the same OpenGL
1423 context as the Qt Quick Scene Graph. This is done by connecting to the
1424 QQuickWindow::beforeRendering() or QQuickWindow::afterRendering()
1425 signal.
1426
1427 \note When using QQuickWindow::beforeRendering(), make sure to
1428 disable clearing before rendering with
1429 QQuickWindow::setClearBeforeRendering().
1430
1431
1432 \section2 Exposure and Visibility
1433
1434 When a QQuickWindow instance is deliberately hidden with hide() or
1435 setVisible(false), it will stop rendering and its scene graph and
1436 graphics context might be released. The sceneGraphInvalidated()
1437 signal will be emitted when this happens.
1438
1439 \warning It is crucial that graphics operations and interaction with
1440 the scene graph happens exclusively on the rendering thread,
1441 primarily during the updatePaintNode() phase.
1442
1443 \warning As signals related to rendering might be emitted from the
1444 rendering thread, connections should be made using
1445 Qt::DirectConnection.
1446
1447
1448 \section2 Resource Management
1449
1450 QML will try to cache images and scene graph nodes to
1451 improve performance, but in some low-memory scenarios it might be
1452 required to aggressively release these resources. The
1453 releaseResources() can be used to force the clean up of certain
1454 resources. Calling releaseResources() may result in the entire
1455 scene graph and in the case of the OpenGL adaptation the associated
1456 context will be deleted. The sceneGraphInvalidated() signal will be
1457 emitted when this happens.
1458
1459 \note All classes with QSG prefix should be used solely on the scene graph's
1460 rendering thread. See \l {Scene Graph and Rendering} for more information.
1461
1462 \section2 Context and Surface Formats
1463
1464 While it is possible to specify a QSurfaceFormat for every QQuickWindow by
1465 calling the member function setFormat(), windows may also be created from
1466 QML by using the Window and ApplicationWindow elements. In this case there
1467 is no C++ code involved in the creation of the window instance, yet
1468 applications may still wish to set certain surface format values, for
1469 example to request a given OpenGL version or profile. Such applications can
1470 call the static function QSurfaceFormat::setDefaultFormat() at startup. The
1471 specified format will be used for all Quick windows created afterwards.
1472
1473 \sa {Scene Graph - OpenGL Under QML}
1474*/
1475
1476/*!
1477 Constructs a window for displaying a QML scene with parent window \a parent.
1478*/
1479QQuickWindow::QQuickWindow(QWindow *parent)
1480 : QQuickWindow(*new QQuickWindowPrivate, parent)
1481{
1482}
1483
1484
1485
1486/*!
1487 \internal
1488*/
1489QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent)
1490 : QWindow(dd, parent)
1491{
1492 Q_D(QQuickWindow);
1493 d->init(c: this);
1494}
1495
1496/*!
1497 \internal
1498*/
1499QQuickWindow::QQuickWindow(QQuickRenderControl *control)
1500 : QWindow(*(new QQuickWindowPrivate), nullptr)
1501{
1502 Q_D(QQuickWindow);
1503 d->init(c: this, control);
1504}
1505
1506/*!
1507 \internal
1508*/
1509QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
1510 : QWindow(dd, nullptr)
1511{
1512 Q_D(QQuickWindow);
1513 d->init(c: this, control);
1514}
1515
1516/*!
1517 Destroys the window.
1518*/
1519QQuickWindow::~QQuickWindow()
1520{
1521 Q_D(QQuickWindow);
1522
1523 if (d->renderControl) {
1524 QQuickRenderControlPrivate::get(renderControl: d->renderControl)->windowDestroyed();
1525 } else if (d->windowManager) {
1526 d->windowManager->removeWindow(win: this);
1527 d->windowManager->windowDestroyed(window: this);
1528 }
1529
1530 delete d->incubationController; d->incubationController = nullptr;
1531#if QT_CONFIG(quick_draganddrop)
1532 delete d->dragGrabber; d->dragGrabber = nullptr;
1533#endif
1534 QQuickRootItem *root = d->contentItem;
1535 d->contentItem = nullptr;
1536 delete root;
1537 qDeleteAll(c: d->pointerEventInstances);
1538 d->pointerEventInstances.clear();
1539
1540 d->renderJobMutex.lock();
1541 qDeleteAll(c: d->beforeSynchronizingJobs);
1542 d->beforeSynchronizingJobs.clear();
1543 qDeleteAll(c: d->afterSynchronizingJobs);
1544 d->afterSynchronizingJobs.clear();
1545 qDeleteAll(c: d->beforeRenderingJobs);
1546 d->beforeRenderingJobs.clear();
1547 qDeleteAll(c: d->afterRenderingJobs);
1548 d->afterRenderingJobs.clear();
1549 qDeleteAll(c: d->afterSwapJobs);
1550 d->afterSwapJobs.clear();
1551 d->renderJobMutex.unlock();
1552
1553 // It is important that the pixmap cache is cleaned up during shutdown.
1554 // Besides playing nice, this also solves a practical problem that
1555 // QQuickTextureFactory implementations in other libraries need
1556 // have their destructors loaded while they the library is still
1557 // loaded into memory.
1558 QQuickPixmap::purgeCache();
1559}
1560
1561/*!
1562 This function tries to release redundant resources currently held by the QML scene.
1563
1564 Calling this function might result in the scene graph and the OpenGL context used
1565 for rendering being released to release graphics memory. If this happens, the
1566 sceneGraphInvalidated() signal will be called, allowing users to clean up their
1567 own graphics resources. The setPersistentOpenGLContext() and setPersistentSceneGraph()
1568 functions can be used to prevent this from happening, if handling the cleanup is
1569 not feasible in the application, at the cost of higher memory usage.
1570
1571 \sa sceneGraphInvalidated(), setPersistentOpenGLContext(), setPersistentSceneGraph()
1572 */
1573
1574void QQuickWindow::releaseResources()
1575{
1576 Q_D(QQuickWindow);
1577 if (d->windowManager)
1578 d->windowManager->releaseResources(window: this);
1579 QQuickPixmap::purgeCache();
1580}
1581
1582
1583
1584/*!
1585 Sets whether the OpenGL context should be preserved, and cannot be
1586 released until the last window is deleted, to \a persistent. The
1587 default value is true.
1588
1589 The OpenGL context can be released to free up graphics resources
1590 when the window is obscured, hidden or not rendering. When this
1591 happens is implementation specific.
1592
1593 The QOpenGLContext::aboutToBeDestroyed() signal is emitted from
1594 the QQuickWindow::openglContext() when the OpenGL context is about
1595 to be released. The QQuickWindow::sceneGraphInitialized() signal
1596 is emitted when a new OpenGL context is created for this
1597 window. Make a Qt::DirectConnection to these signals to be
1598 notified.
1599
1600 The OpenGL context is still released when the last QQuickWindow is
1601 deleted.
1602
1603 \note This only has an effect when using the default OpenGL scene
1604 graph adaptation.
1605
1606 \sa setPersistentSceneGraph(),
1607 QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized()
1608 */
1609
1610void QQuickWindow::setPersistentOpenGLContext(bool persistent)
1611{
1612 Q_D(QQuickWindow);
1613 d->persistentGLContext = persistent;
1614}
1615
1616
1617
1618/*!
1619 Returns whether the OpenGL context can be released during the
1620 lifetime of the QQuickWindow.
1621
1622 \note This is a hint. When and how this happens is implementation
1623 specific. It also only has an effect when using the default OpenGL
1624 scene graph adaptation
1625 */
1626
1627bool QQuickWindow::isPersistentOpenGLContext() const
1628{
1629 Q_D(const QQuickWindow);
1630 return d->persistentGLContext;
1631}
1632
1633
1634
1635/*!
1636 Sets whether the scene graph nodes and resources are \a persistent.
1637 Persistent means the nodes and resources cannot be released.
1638 The default value is \c true.
1639
1640 The scene graph nodes and resources can be released to free up
1641 graphics resources when the window is obscured, hidden or not
1642 rendering. When this happens is implementation specific.
1643
1644 The QQuickWindow::sceneGraphInvalidated() signal is emitted when
1645 cleanup occurs. The QQuickWindow::sceneGraphInitialized() signal
1646 is emitted when a new scene graph is recreated for this
1647 window. Make a Qt::DirectConnection to these signals to be
1648 notified.
1649
1650 The scene graph nodes and resources are still released when the
1651 last QQuickWindow is deleted.
1652
1653 \sa setPersistentOpenGLContext(),
1654 sceneGraphInvalidated(), sceneGraphInitialized()
1655 */
1656
1657void QQuickWindow::setPersistentSceneGraph(bool persistent)
1658{
1659 Q_D(QQuickWindow);
1660 d->persistentSceneGraph = persistent;
1661}
1662
1663
1664
1665/*!
1666 Returns whether the scene graph nodes and resources can be
1667 released during the lifetime of this QQuickWindow.
1668
1669 \note This is a hint. When and how this happens is implementation
1670 specific.
1671 */
1672
1673bool QQuickWindow::isPersistentSceneGraph() const
1674{
1675 Q_D(const QQuickWindow);
1676 return d->persistentSceneGraph;
1677}
1678
1679
1680
1681
1682/*!
1683 \qmlattachedproperty Item Window::contentItem
1684 \since 5.4
1685
1686 This attached property holds the invisible root item of the scene or
1687 \c null if the item is not in a window. The Window attached property
1688 can be attached to any Item.
1689*/
1690
1691/*!
1692 \property QQuickWindow::contentItem
1693 \brief The invisible root item of the scene.
1694
1695 A QQuickWindow always has a single invisible root item containing all of its content.
1696 To add items to this window, reparent the items to the contentItem or to an existing
1697 item in the scene.
1698*/
1699QQuickItem *QQuickWindow::contentItem() const
1700{
1701 Q_D(const QQuickWindow);
1702
1703 return d->contentItem;
1704}
1705
1706/*!
1707 \property QQuickWindow::activeFocusItem
1708
1709 \brief The item which currently has active focus or \c null if there is
1710 no item with active focus.
1711
1712 \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick}
1713*/
1714QQuickItem *QQuickWindow::activeFocusItem() const
1715{
1716 Q_D(const QQuickWindow);
1717
1718 return d->activeFocusItem;
1719}
1720
1721/*!
1722 \internal
1723 \reimp
1724*/
1725QObject *QQuickWindow::focusObject() const
1726{
1727 Q_D(const QQuickWindow);
1728
1729 if (d->activeFocusItem)
1730 return d->activeFocusItem;
1731 return const_cast<QQuickWindow*>(this);
1732}
1733
1734
1735/*!
1736 Returns the item which currently has the mouse grab.
1737*/
1738QQuickItem *QQuickWindow::mouseGrabberItem() const
1739{
1740 Q_D(const QQuickWindow);
1741
1742 if (d->isDeliveringTouchAsMouse()) {
1743 if (QQuickPointerEvent *event = d->queryPointerEventInstance(device: d->touchMouseDevice)) {
1744 auto point = event->pointById(pointId: d->touchMouseId);
1745 return point ? point->grabberItem() : nullptr;
1746 }
1747 } else if (QQuickPointerEvent *event = d->queryPointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())) {
1748 Q_ASSERT(event->pointCount());
1749 return event->point(i: 0)->grabberItem();
1750 }
1751 return nullptr;
1752}
1753
1754
1755bool QQuickWindowPrivate::clearHover(ulong timestamp)
1756{
1757 Q_Q(QQuickWindow);
1758 if (hoverItems.isEmpty())
1759 return false;
1760
1761 QPointF pos = q->mapFromGlobal(pos: QGuiApplicationPrivate::lastCursorPosition.toPoint());
1762
1763 bool accepted = false;
1764 for (QQuickItem* item : qAsConst(t&: hoverItems)) {
1765 accepted = sendHoverEvent(QEvent::HoverLeave, item, scenePos: pos, lastScenePos: pos, modifiers: QGuiApplication::keyboardModifiers(), timestamp, accepted: true) || accepted;
1766#if QT_CONFIG(cursor)
1767 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1768 if (itemPrivate->hasPointerHandlers()) {
1769 pos = q->mapFromGlobal(pos: QCursor::pos());
1770 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
1771 pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: pos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp, velocity: QVector2D());
1772 pointerEvent->point(i: 0)->setAccepted(true);
1773 pointerEvent->localize(target: item);
1774 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
1775 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(object: h))
1776 hh->handlePointerEvent(event: pointerEvent);
1777 }
1778#endif
1779 }
1780 hoverItems.clear();
1781 return accepted;
1782}
1783
1784/*! \reimp */
1785bool QQuickWindow::event(QEvent *e)
1786{
1787 Q_D(QQuickWindow);
1788
1789 switch (e->type()) {
1790
1791 case QEvent::TouchBegin:
1792 case QEvent::TouchUpdate:
1793 case QEvent::TouchEnd: {
1794 QTouchEvent *touch = static_cast<QTouchEvent*>(e);
1795 d->handleTouchEvent(touch);
1796 if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
1797 // we consume all touch events ourselves to avoid duplicate
1798 // mouse delivery by QtGui mouse synthesis
1799 e->accept();
1800 }
1801 return true;
1802 }
1803 break;
1804 case QEvent::TouchCancel:
1805 // return in order to avoid the QWindow::event below
1806 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
1807 break;
1808 case QEvent::Enter: {
1809 if (!d->contentItem)
1810 return false;
1811 QEnterEvent *enter = static_cast<QEnterEvent*>(e);
1812 bool accepted = enter->isAccepted();
1813 bool delivered = d->deliverHoverEvent(d->contentItem, scenePos: enter->windowPos(), lastScenePos: d->lastMousePosition,
1814 modifiers: QGuiApplication::keyboardModifiers(), timestamp: 0L, accepted);
1815 d->lastMousePosition = enter->windowPos();
1816 enter->setAccepted(accepted);
1817#if QT_CONFIG(cursor)
1818 d->updateCursor(scenePos: mapFromGlobal(pos: QCursor::pos()));
1819#endif
1820 return delivered;
1821 }
1822 break;
1823 case QEvent::Leave:
1824 d->clearHover();
1825 d->lastMousePosition = QPointF();
1826 break;
1827#if QT_CONFIG(quick_draganddrop)
1828 case QEvent::DragEnter:
1829 case QEvent::DragLeave:
1830 case QEvent::DragMove:
1831 case QEvent::Drop:
1832 d->deliverDragEvent(d->dragGrabber, e);
1833 break;
1834#endif
1835 case QEvent::WindowDeactivate:
1836 if (d->contentItem)
1837 d->contentItem->windowDeactivateEvent();
1838 break;
1839 case QEvent::Close: {
1840 // TOOD Qt 6 (binary incompatible)
1841 // closeEvent(static_cast<QCloseEvent *>(e));
1842 QQuickCloseEvent qev;
1843 qev.setAccepted(e->isAccepted());
1844 emit closing(close: &qev);
1845 e->setAccepted(qev.isAccepted());
1846 } break;
1847 case QEvent::PlatformSurface:
1848 if ((static_cast<QPlatformSurfaceEvent *>(e))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
1849 // Ensure that the rendering thread is notified before
1850 // the QPlatformWindow is destroyed.
1851 if (d->windowManager)
1852 d->windowManager->hide(window: this);
1853 }
1854 break;
1855 case QEvent::FocusAboutToChange:
1856#if QT_CONFIG(im)
1857 if (d->activeFocusItem)
1858 qGuiApp->inputMethod()->commit();
1859#endif
1860 if (mouseGrabberItem())
1861 mouseGrabberItem()->ungrabMouse();
1862 break;
1863 case QEvent::UpdateRequest: {
1864 if (d->windowManager)
1865 d->windowManager->handleUpdateRequest(this);
1866 break;
1867 }
1868#if QT_CONFIG(gestures)
1869 case QEvent::NativeGesture:
1870 d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(ev: e));
1871 break;
1872#endif
1873 case QEvent::ShortcutOverride:
1874 if (d->activeFocusItem)
1875 QCoreApplication::sendEvent(receiver: d->activeFocusItem, event: e);
1876 return true;
1877 case QEvent::LanguageChange:
1878 if (d->contentItem)
1879 QCoreApplication::sendEvent(receiver: d->contentItem, event: e);
1880 break;
1881 case QEvent::InputMethod:
1882 case QEvent::InputMethodQuery:
1883 {
1884 QQuickItem *target = d->activeFocusItem;
1885 // while an input method delivers the event, this window might still be inactive
1886 if (!target) {
1887 target = d->contentItem;
1888 if (!target || !target->isEnabled())
1889 break;
1890 // see setFocusInScope for a similar loop
1891 while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
1892 target = target->scopedFocusItem();
1893 }
1894 if (target) {
1895 QCoreApplication::sendEvent(receiver: target, event: e);
1896 return true;
1897 }
1898 }
1899 break;
1900 default:
1901 break;
1902 }
1903
1904 if (e->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest))
1905 update();
1906 else if (e->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure))
1907 d->windowManager->handleContextCreationFailure(window: this);
1908
1909 return QWindow::event(e);
1910}
1911
1912/*! \reimp */
1913void QQuickWindow::keyPressEvent(QKeyEvent *e)
1914{
1915 Q_D(QQuickWindow);
1916 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(),
1917 e->modifiers());
1918 d->deliverKeyEvent(e);
1919}
1920
1921/*! \reimp */
1922void QQuickWindow::keyReleaseEvent(QKeyEvent *e)
1923{
1924 Q_D(QQuickWindow);
1925 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(),
1926 e->modifiers());
1927 d->deliverKeyEvent(e);
1928}
1929
1930void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e)
1931{
1932 if (activeFocusItem) {
1933 QQuickItem *item = activeFocusItem;
1934
1935 // In case of generated event, trigger ShortcutOverride event
1936 if (e->type() == QEvent::KeyPress && e->spontaneous() == false)
1937 qt_sendShortcutOverrideEvent(o: item, timestamp: e->timestamp(),
1938 k: e->key(), mods: e->modifiers(), text: e->text(),
1939 autorep: e->isAutoRepeat(), count: e->count());
1940
1941 e->accept();
1942 QCoreApplication::sendEvent(receiver: item, event: e);
1943 while (!e->isAccepted() && (item = item->parentItem())) {
1944 e->accept();
1945 QCoreApplication::sendEvent(receiver: item, event: e);
1946 }
1947 }
1948}
1949
1950QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos)
1951{
1952 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
1953 QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event);
1954 QMouseEvent *me = new QMouseEvent(event->type(),
1955 transformedLocalPos ? *transformedLocalPos : event->localPos(),
1956 event->windowPos(), event->screenPos(),
1957 event->button(), event->buttons(), event->modifiers());
1958 QGuiApplicationPrivate::setMouseEventCapsAndVelocity(event: me, caps, velocity);
1959 QGuiApplicationPrivate::setMouseEventSource(event: me, source: QGuiApplicationPrivate::mouseEventSource(event));
1960 me->setTimestamp(event->timestamp());
1961 return me;
1962}
1963
1964void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuickPointerHandler> > &passiveGrabbers,
1965 QQuickPointerEvent *pointerEvent)
1966{
1967 const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
1968 QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
1969 hasFiltered.clear();
1970 for (auto handler : passiveGrabbers) {
1971 // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
1972 if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(t: handler)) {
1973 bool alreadyFiltered = false;
1974 QQuickItem *par = handler->parentItem();
1975
1976 // see if we already have sent a filter event to the parent
1977 auto it = std::find_if(first: sendFilteredPointerEventResult.begin(), last: sendFilteredPointerEventResult.end(),
1978 pred: [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; });
1979 if (it != sendFilteredPointerEventResult.end()) {
1980 // Yes, the event was already filtered to that parent, do not call it again but use
1981 // the result of the previous call to determine if we should call the handler.
1982 alreadyFiltered = it->second;
1983 } else {
1984 alreadyFiltered = sendFilteredPointerEvent(event: pointerEvent, receiver: par);
1985 sendFilteredPointerEventResult << qMakePair<QQuickItem*, bool>(x: par, y: alreadyFiltered);
1986 }
1987 if (!alreadyFiltered) {
1988 pointerEvent->localize(target: handler->parentItem());
1989 handler->handlePointerEvent(event: pointerEvent);
1990 }
1991 }
1992 }
1993}
1994
1995
1996
1997void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent)
1998{
1999 Q_Q(QQuickWindow);
2000 auto point = pointerEvent->point(i: 0);
2001 lastMousePosition = point->scenePosition();
2002 const bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton);
2003 QQuickItem *grabberItem = point->grabberItem();
2004 if (!grabberItem && isDeliveringTouchAsMouse())
2005 grabberItem = q->mouseGrabberItem();
2006
2007 if (grabberItem) {
2008 bool handled = false;
2009 hasFiltered.clear();
2010 if (sendFilteredPointerEvent(event: pointerEvent, receiver: grabberItem))
2011 handled = true;
2012 // if the grabber is an Item:
2013 // if the update consists of changing button state, don't accept it unless
2014 // the button is one in which the grabber is interested
2015 Qt::MouseButtons acceptedButtons = grabberItem->acceptedMouseButtons();
2016 if (!handled && pointerEvent->button() != Qt::NoButton && acceptedButtons
2017 && !(acceptedButtons & pointerEvent->button())) {
2018 pointerEvent->setAccepted(false);
2019 handled = true;
2020 }
2021
2022 // send update
2023 if (!handled) {
2024 QPointF localPos = grabberItem->mapFromScene(point: lastMousePosition);
2025 auto me = pointerEvent->asMouseEvent(localPos);
2026 me->accept();
2027 QCoreApplication::sendEvent(receiver: grabberItem, event: me);
2028 point->setAccepted(me->isAccepted());
2029 }
2030
2031 // release event: ungrab if no buttons are pressed anymore
2032 if (mouseIsReleased)
2033 removeGrabber(grabber: grabberItem, mouse: true, touch: isDeliveringTouchAsMouse());
2034 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent);
2035 } else if (auto handler = point->grabberPointerHandler()) {
2036 pointerEvent->localize(target: handler->parentItem());
2037 hasFiltered.clear();
2038 if (!sendFilteredPointerEvent(event: pointerEvent, receiver: handler->parentItem()))
2039 handler->handlePointerEvent(event: pointerEvent);
2040 if (mouseIsReleased)
2041 point->setGrabberPointerHandler(exclusiveGrabber: nullptr, exclusive: true);
2042 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent);
2043 } else {
2044 bool delivered = false;
2045 if (pointerEvent->isPressEvent()) {
2046 // send initial press
2047 delivered = deliverPressOrReleaseEvent(pointerEvent);
2048 } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) {
2049 // if this is an update or release from an actual mouse,
2050 // and the point wasn't grabbed, deliver only to PointerHandlers:
2051 // passive grabbers first, then the rest
2052 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent);
2053
2054 // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
2055 if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
2056 QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false);
2057 for (QQuickItem *item : targetItems) {
2058 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2059 if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty())
2060 continue;
2061 pointerEvent->localize(target: item);
2062 hasFiltered.clear();
2063 if (!sendFilteredPointerEvent(event: pointerEvent, receiver: item)) {
2064 if (itemPrivate->handlePointerEvent(pointerEvent, avoidExclusiveGrabber: true)) // avoid re-delivering to grabbers
2065 delivered = true;
2066 }
2067 if (point->exclusiveGrabber())
2068 break;
2069 }
2070 }
2071 }
2072
2073 if (!delivered)
2074 // make sure not to accept unhandled events
2075 pointerEvent->setAccepted(false);
2076 }
2077}
2078
2079bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
2080 const QPointF &scenePos, const QPointF &lastScenePos,
2081 Qt::KeyboardModifiers modifiers, ulong timestamp,
2082 bool accepted)
2083{
2084 const QTransform transform = QQuickItemPrivate::get(item)->windowToItemTransform();
2085
2086 //create copy of event
2087 QHoverEvent hoverEvent(type, transform.map(p: scenePos), transform.map(p: lastScenePos), modifiers);
2088 hoverEvent.setTimestamp(timestamp);
2089 hoverEvent.setAccepted(accepted);
2090
2091 hasFiltered.clear();
2092 if (sendFilteredMouseEvent(event: &hoverEvent, receiver: item, filteringParent: item->parentItem()))
2093 return true;
2094
2095 QCoreApplication::sendEvent(receiver: item, event: &hoverEvent);
2096
2097 return hoverEvent.isAccepted();
2098}
2099
2100bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
2101 Qt::KeyboardModifiers modifiers, ulong timestamp, bool &accepted)
2102{
2103 Q_Q(QQuickWindow);
2104 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2105
2106 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
2107 QPointF p = item->mapFromScene(point: scenePos);
2108 if (!item->contains(point: p))
2109 return false;
2110 }
2111
2112 qCDebug(DBG_HOVER_TRACE) << q << item << scenePos << lastScenePos << "subtreeHoverEnabled" << itemPrivate->subtreeHoverEnabled;
2113 if (itemPrivate->subtreeHoverEnabled) {
2114 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
2115 for (int ii = children.count() - 1; ii >= 0; --ii) {
2116 QQuickItem *child = children.at(i: ii);
2117 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled)
2118 continue;
2119 if (deliverHoverEvent(item: child, scenePos, lastScenePos, modifiers, timestamp, accepted))
2120 return true;
2121 }
2122 }
2123
2124 if (itemPrivate->hasPointerHandlers()) {
2125 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
2126 pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: scenePos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp, velocity: QVector2D());
2127 pointerEvent->point(i: 0)->setAccepted(true);
2128 pointerEvent->localize(target: item);
2129 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
2130 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(object: h))
2131 hh->handlePointerEvent(event: pointerEvent);
2132 }
2133
2134 if (itemPrivate->hoverEnabled) {
2135 QPointF p = item->mapFromScene(point: scenePos);
2136 if (item->contains(point: p)) {
2137 if (!hoverItems.isEmpty() && hoverItems.at(i: 0) == item) {
2138 //move
2139 accepted = sendHoverEvent(type: QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
2140 } else {
2141 QList<QQuickItem *> itemsToHover;
2142 QQuickItem* parent = item;
2143 itemsToHover << item;
2144 while ((parent = parent->parentItem()))
2145 itemsToHover << parent;
2146
2147 // Leaving from previous hovered items until we reach the item or one of its ancestors.
2148 while (!hoverItems.isEmpty() && !itemsToHover.contains(t: hoverItems.at(i: 0))) {
2149 QQuickItem *hoverLeaveItem = hoverItems.takeFirst();
2150 sendHoverEvent(type: QEvent::HoverLeave, item: hoverLeaveItem, scenePos, lastScenePos, modifiers, timestamp, accepted);
2151 QQuickItemPrivate *hoverLeaveItemPrivate = QQuickItemPrivate::get(item: hoverLeaveItem);
2152 if (hoverLeaveItemPrivate->hasPointerHandlers()) {
2153 for (QQuickPointerHandler *handler : hoverLeaveItemPrivate->extra->pointerHandlers) {
2154 if (auto *hh = qmlobject_cast<QQuickHoverHandler *>(object: handler)) {
2155 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
2156 pointerEvent->point(i: 0)->cancelPassiveGrab(handler: hh);
2157 }
2158 }
2159 }
2160 }
2161
2162 if (!hoverItems.isEmpty() && hoverItems.at(i: 0) == item) {//Not entering a new Item
2163 // ### Shouldn't we send moves for the parent items as well?
2164 accepted = sendHoverEvent(type: QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp, accepted);
2165 } else {
2166 // Enter items that are not entered yet.
2167 int startIdx = -1;
2168 if (!hoverItems.isEmpty())
2169 startIdx = itemsToHover.indexOf(t: hoverItems.at(i: 0)) - 1;
2170 if (startIdx == -1)
2171 startIdx = itemsToHover.count() - 1;
2172
2173 for (int i = startIdx; i >= 0; i--) {
2174 QQuickItem *itemToHover = itemsToHover.at(i);
2175 QQuickItemPrivate *itemToHoverPrivate = QQuickItemPrivate::get(item: itemToHover);
2176 // The item may be about to be deleted or reparented to another window
2177 // due to another hover event delivered in this function. If that is the
2178 // case, sending a hover event here will cause a crash or other bad
2179 // behavior when the leave event is generated. Checking
2180 // itemToHoverPrivate->window here prevents that case.
2181 if (itemToHoverPrivate->window == q && itemToHoverPrivate->hoverEnabled) {
2182 hoverItems.prepend(t: itemToHover);
2183 sendHoverEvent(type: QEvent::HoverEnter, item: itemToHover, scenePos, lastScenePos, modifiers, timestamp, accepted);
2184 }
2185 }
2186 }
2187 }
2188 return true;
2189 }
2190 }
2191
2192 return false;
2193}
2194
2195// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
2196// in the usual reverse-paint-order until propagation is stopped
2197bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event)
2198{
2199 Q_ASSERT(event->pointCount() == 1);
2200 QQuickEventPoint *point = event->point(i: 0);
2201 QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false);
2202
2203 for (QQuickItem *item : targetItems) {
2204 if (!item->window())
2205 continue;
2206 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2207 event->localize(target: item);
2208 // Let Pointer Handlers have the first shot
2209 itemPrivate->handlePointerEvent(event);
2210 if (point->isAccepted())
2211 return true;
2212 QPointF g = item->window()->mapToGlobal(pos: point->scenePosition().toPoint());
2213#if QT_CONFIG(wheelevent)
2214 // Let the Item have a chance to handle it
2215 if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) {
2216 QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(),
2217 pse->buttons(), pse->modifiers(), pse->phase(),
2218 pse->isInverted(), pse->synthSource());
2219 wheel.setTimestamp(pse->timestamp());
2220 wheel.accept();
2221 QCoreApplication::sendEvent(receiver: item, event: &wheel);
2222 if (wheel.isAccepted()) {
2223 qCDebug(lcWheelTarget) << &wheel << "->" << item;
2224 event->setAccepted(true);
2225 return true;
2226 }
2227 }
2228#endif
2229#if QT_CONFIG(gestures)
2230 if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) {
2231 QNativeGestureEvent nge(pnge->type(), pnge->device()->qTouchDevice(), point->position(), point->scenePosition(), g,
2232 pnge->value(), 0L, 0L); // TODO can't copy things I can't access
2233 nge.accept();
2234 QCoreApplication::sendEvent(receiver: item, event: &nge);
2235 if (nge.isAccepted()) {
2236 qCDebug(lcGestureTarget) << &nge << "->" << item;
2237 event->setAccepted(true);
2238 return true;
2239 }
2240 }
2241#endif // gestures
2242 }
2243
2244 return false; // it wasn't handled
2245}
2246
2247#if QT_CONFIG(wheelevent)
2248/*! \reimp */
2249void QQuickWindow::wheelEvent(QWheelEvent *event)
2250{
2251 Q_D(QQuickWindow);
2252 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
2253 event->angleDelta().x(), event->angleDelta().y());
2254
2255 qCDebug(DBG_MOUSE) << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta() << event->phase();
2256
2257 //if the actual wheel event was accepted, accept the compatibility wheel event and return early
2258 if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
2259 return;
2260
2261 event->ignore();
2262 d->deliverPointerEvent(d->pointerEventInstance(ev: event));
2263 d->lastWheelEventAccepted = event->isAccepted();
2264}
2265#endif // wheelevent
2266
2267#if QT_CONFIG(tabletevent)
2268/*! \reimp */
2269void QQuickWindow::tabletEvent(QTabletEvent *event)
2270{
2271 Q_D(QQuickWindow);
2272 qCDebug(lcTablet) << event;
2273 // TODO Qt 6: make sure TabletEnterProximity and TabletLeaveProximity are delivered here
2274 d->deliverPointerEvent(d->pointerEventInstance(ev: event));
2275}
2276#endif // tabletevent
2277
2278bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
2279{
2280 qCDebug(DBG_TOUCH) << event;
2281 Q_Q(QQuickWindow);
2282
2283 if (QQuickItem *grabber = q->mouseGrabberItem())
2284 sendUngrabEvent(grabber, touch: true);
2285 cancelTouchMouseSynthesis();
2286
2287 // A TouchCancel event will typically not contain any points.
2288 // Deliver it to all items and handlers that have active touches.
2289 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::touchDevice(d: event->device()));
2290 for (int i = 0; i < pointerEvent->pointCount(); ++i)
2291 pointerEvent->point(i)->cancelExclusiveGrabImpl(cancelEvent: event);
2292
2293 // The next touch event can only be a TouchBegin, so clean up.
2294 pointerEvent->clearGrabbers();
2295 return true;
2296}
2297
2298void QQuickWindowPrivate::deliverDelayedTouchEvent()
2299{
2300 // Deliver and delete delayedTouch.
2301 // Set delayedTouch to 0 before delivery to avoid redelivery in case of
2302 // event loop recursions (e.g if it the touch starts a dnd session).
2303 QScopedPointer<QTouchEvent> e(delayedTouch.take());
2304 deliverPointerEvent(pointerEventInstance(ev: e.data()));
2305}
2306
2307bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
2308{
2309 Q_Q(QQuickWindow);
2310 Qt::TouchPointStates states = event->touchPointStates();
2311 if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) == 0)
2312 || ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) != 0)) {
2313 // we can only compress something that isn't a press or release
2314 return false;
2315 }
2316
2317 if (!delayedTouch) {
2318 delayedTouch.reset(other: new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
2319 delayedTouch->setTimestamp(event->timestamp());
2320 if (renderControl)
2321 QQuickRenderControlPrivate::get(renderControl)->maybeUpdate();
2322 else if (windowManager)
2323 windowManager->maybeUpdate(window: q);
2324 return true;
2325 }
2326
2327 // check if this looks like the last touch event
2328 if (delayedTouch->type() == event->type() &&
2329 delayedTouch->device() == event->device() &&
2330 delayedTouch->modifiers() == event->modifiers() &&
2331 delayedTouch->touchPoints().count() == event->touchPoints().count())
2332 {
2333 // possible match.. is it really the same?
2334 bool mismatch = false;
2335
2336 QList<QTouchEvent::TouchPoint> tpts = event->touchPoints();
2337 Qt::TouchPointStates states;
2338 for (int i = 0; i < event->touchPoints().count(); ++i) {
2339 const QTouchEvent::TouchPoint &tp = tpts.at(i);
2340 const QTouchEvent::TouchPoint &tpDelayed = delayedTouch->touchPoints().at(i);
2341 if (tp.id() != tpDelayed.id()) {
2342 mismatch = true;
2343 break;
2344 }
2345
2346 if (tpDelayed.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary)
2347 tpts[i].setState(Qt::TouchPointMoved);
2348 tpts[i].setLastPos(tpDelayed.lastPos());
2349 tpts[i].setLastScenePos(tpDelayed.lastScenePos());
2350 tpts[i].setLastScreenPos(tpDelayed.lastScreenPos());
2351 tpts[i].setLastNormalizedPos(tpDelayed.lastNormalizedPos());
2352
2353 states |= tpts.at(i).state();
2354 }
2355
2356 // matching touch event? then merge the new event into the old one
2357 if (!mismatch) {
2358 delayedTouch->setTouchPoints(tpts);
2359 delayedTouch->setTimestamp(event->timestamp());
2360 return true;
2361 }
2362 }
2363
2364 // merging wasn't possible, so deliver the delayed event first, and then delay this one
2365 deliverDelayedTouchEvent();
2366 delayedTouch.reset(other: new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()));
2367 delayedTouch->setTimestamp(event->timestamp());
2368 return true;
2369}
2370
2371// entry point for touch event delivery:
2372// - translate the event to window coordinates
2373// - compress the event instead of delivering it if applicable
2374// - call deliverTouchPoints to actually dispatch the points
2375void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
2376{
2377 translateTouchEvent(touchEvent: event);
2378 if (event->touchPoints().size()) {
2379 auto point = event->touchPoints().at(i: 0);
2380 if (point.state() == Qt::TouchPointReleased) {
2381 lastMousePosition = QPointF();
2382 } else {
2383 lastMousePosition = point.pos();
2384 }
2385 }
2386
2387 qCDebug(DBG_TOUCH) << event;
2388
2389 static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet(varName: "QML_NO_TOUCH_COMPRESSION");
2390
2391 if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
2392 deliverPointerEvent(pointerEventInstance(ev: event));
2393 return;
2394 }
2395
2396 if (!compressTouchEvent(event)) {
2397 if (delayedTouch)
2398 deliverDelayedTouchEvent();
2399 deliverPointerEvent(pointerEventInstance(ev: event));
2400 }
2401}
2402
2403/*! \reimp */
2404void QQuickWindow::mousePressEvent(QMouseEvent *event)
2405{
2406 Q_D(QQuickWindow);
2407 d->handleMouseEvent(event);
2408}
2409/*! \reimp */
2410void QQuickWindow::mouseMoveEvent(QMouseEvent *event)
2411{
2412 Q_D(QQuickWindow);
2413 d->handleMouseEvent(event);
2414}
2415/*! \reimp */
2416void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event)
2417{
2418 Q_D(QQuickWindow);
2419 d->handleMouseEvent(event);
2420}
2421/*! \reimp */
2422void QQuickWindow::mouseReleaseEvent(QMouseEvent *event)
2423{
2424 Q_D(QQuickWindow);
2425 d->handleMouseEvent(event);
2426}
2427
2428void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
2429{
2430 if (event->source() == Qt::MouseEventSynthesizedBySystem) {
2431 event->accept();
2432 return;
2433 }
2434 qCDebug(DBG_MOUSE) << "QQuickWindow::handleMouseEvent()" << event->type() << event->localPos() << event->button() << event->buttons();
2435
2436 switch (event->type()) {
2437 case QEvent::MouseButtonPress:
2438 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
2439 event->buttons());
2440 deliverPointerEvent(pointerEventInstance(ev: event));
2441 break;
2442 case QEvent::MouseButtonRelease:
2443 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
2444 event->buttons());
2445 deliverPointerEvent(pointerEventInstance(ev: event));
2446#if QT_CONFIG(cursor)
2447 updateCursor(scenePos: event->windowPos());
2448#endif
2449 break;
2450 case QEvent::MouseButtonDblClick:
2451 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
2452 event->button(), event->buttons());
2453 if (allowDoubleClick)
2454 deliverPointerEvent(pointerEventInstance(ev: event));
2455 break;
2456 case QEvent::MouseMove:
2457 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
2458 event->localPos().x(), event->localPos().y());
2459
2460 qCDebug(DBG_HOVER_TRACE) << this;
2461
2462 #if QT_CONFIG(cursor)
2463 updateCursor(scenePos: event->windowPos());
2464 #endif
2465
2466 if (!pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice())->point(i: 0)->exclusiveGrabber()) {
2467 QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
2468 lastMousePosition = event->windowPos();
2469
2470 bool accepted = event->isAccepted();
2471 bool delivered = deliverHoverEvent(item: contentItem, scenePos: event->windowPos(), lastScenePos: last, modifiers: event->modifiers(), timestamp: event->timestamp(), accepted);
2472 if (!delivered) {
2473 //take care of any exits
2474 accepted = clearHover(timestamp: event->timestamp());
2475 }
2476 event->setAccepted(accepted);
2477 }
2478 deliverPointerEvent(pointerEventInstance(ev: event));
2479 break;
2480 default:
2481 Q_ASSERT(false);
2482 break;
2483 }
2484}
2485
2486void QQuickWindowPrivate::flushFrameSynchronousEvents()
2487{
2488 Q_Q(QQuickWindow);
2489
2490 if (delayedTouch) {
2491 deliverDelayedTouchEvent();
2492
2493 // Touch events which constantly start animations (such as a behavior tracking
2494 // the mouse point) need animations to start.
2495 QQmlAnimationTimer *ut = QQmlAnimationTimer::instance();
2496 if (ut && ut->hasStartAnimationPending())
2497 ut->startAnimations();
2498 }
2499
2500 // In webOS we already have the alternative to the issue that this
2501 // wanted to address and thus skipping this part won't break anything.
2502#if !defined(Q_OS_WEBOS)
2503 // Once per frame, if any items are dirty, send a synthetic hover,
2504 // in case items have changed position, visibility, etc.
2505 // For instance, during animation (including the case of a ListView
2506 // whose delegates contain MouseAreas), a MouseArea needs to know
2507 // whether it has moved into a position where it is now under the cursor.
2508 if (!q->mouseGrabberItem() && !lastMousePosition.isNull() && dirtyItemList) {
2509 bool accepted = false;
2510 bool delivered = deliverHoverEvent(item: contentItem, scenePos: lastMousePosition, lastScenePos: lastMousePosition, modifiers: QGuiApplication::keyboardModifiers(), timestamp: 0, accepted);
2511 if (!delivered)
2512 clearHover(); // take care of any exits
2513 }
2514#endif
2515}
2516
2517QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const
2518{
2519 // Search for a matching reusable event object.
2520 for (QQuickPointerEvent *e : pointerEventInstances) {
2521 // If device can generate native gestures (e.g. a trackpad), there might be multiple QQuickPointerEvents:
2522 // QQuickPointerNativeGestureEvent, QQuickPointerScrollEvent, and QQuickPointerTouchEvent.
2523 // Use eventType to disambiguate.
2524#if QT_CONFIG(gestures)
2525 if ((eventType == QEvent::NativeGesture) != bool(e->asPointerNativeGestureEvent()))
2526 continue;
2527#endif
2528 if ((eventType == QEvent::Wheel) != bool(e->asPointerScrollEvent()))
2529 continue;
2530 // Otherwise we assume there's only one event type per device.
2531 // More disambiguation tests might need to be added above if that changes later.
2532 if (e->device() == device)
2533 return e;
2534 }
2535 return nullptr;
2536}
2537
2538QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const
2539{
2540 QQuickPointerEvent *ev = queryPointerEventInstance(device, eventType);
2541 if (ev)
2542 return ev;
2543 QQuickWindow *q = const_cast<QQuickWindow*>(q_func());
2544 switch (device->type()) {
2545 case QQuickPointerDevice::Mouse:
2546 // QWindowSystemInterface::handleMouseEvent() does not take a device parameter:
2547 // we assume all mouse events come from one mouse (the "core pointer").
2548 // So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice()
2549 if (eventType == QEvent::Wheel)
2550 ev = new QQuickPointerScrollEvent(q, device);
2551 else
2552 ev = new QQuickPointerMouseEvent(q, device);
2553 break;
2554 case QQuickPointerDevice::TouchPad:
2555 case QQuickPointerDevice::TouchScreen:
2556#if QT_CONFIG(gestures)
2557 if (eventType == QEvent::NativeGesture)
2558 ev = new QQuickPointerNativeGestureEvent(q, device);
2559 else // assume QEvent::Type is one of TouchBegin/Update/End
2560#endif
2561 ev = new QQuickPointerTouchEvent(q, device);
2562 break;
2563#if QT_CONFIG(tabletevent)
2564 case QQuickPointerDevice::Stylus:
2565 case QQuickPointerDevice::Airbrush:
2566 case QQuickPointerDevice::Puck:
2567 ev = new QQuickPointerTabletEvent(q, device);
2568 break;
2569#endif
2570 default:
2571 break;
2572 }
2573 pointerEventInstances << ev;
2574 return ev;
2575}
2576
2577/*!
2578 \internal
2579 Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event.
2580
2581 There is a unique instance per QQuickPointerDevice, which is determined
2582 from \a event's device.
2583*/
2584QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const
2585{
2586 QQuickPointerDevice *dev = nullptr;
2587 switch (event->type()) {
2588 case QEvent::MouseButtonPress:
2589 case QEvent::MouseButtonRelease:
2590 case QEvent::MouseButtonDblClick:
2591 case QEvent::MouseMove:
2592 case QEvent::Wheel:
2593 dev = QQuickPointerDevice::genericMouseDevice();
2594 break;
2595 case QEvent::TouchBegin:
2596 case QEvent::TouchUpdate:
2597 case QEvent::TouchEnd:
2598 case QEvent::TouchCancel:
2599 dev = QQuickPointerDevice::touchDevice(d: static_cast<QTouchEvent *>(event)->device());
2600 break;
2601#if QT_CONFIG(tabletevent)
2602 case QEvent::TabletPress:
2603 case QEvent::TabletMove:
2604 case QEvent::TabletRelease:
2605 case QEvent::TabletEnterProximity:
2606 case QEvent::TabletLeaveProximity:
2607 dev = QQuickPointerDevice::tabletDevice(event: static_cast<QTabletEvent *>(event));
2608 break;
2609#endif
2610#if QT_CONFIG(gestures)
2611 case QEvent::NativeGesture:
2612 dev = QQuickPointerDevice::touchDevice(d: static_cast<QNativeGestureEvent *>(event)->device());
2613 break;
2614#endif
2615 default:
2616 break;
2617 }
2618
2619 Q_ASSERT(dev);
2620 auto pev = pointerEventInstance(device: dev, eventType: event->type());
2621 Q_ASSERT(pev);
2622 return pev->reset(ev: event);
2623}
2624
2625void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
2626{
2627 Q_Q(QQuickWindow);
2628 // If users spin the eventloop as a result of event delivery, we disable
2629 // event compression and send events directly. This is because we consider
2630 // the usecase a bit evil, but we at least don't want to lose events.
2631 ++pointerEventRecursionGuard;
2632
2633 skipDelivery.clear();
2634 if (event->asPointerMouseEvent()) {
2635 deliverMouseEvent(pointerEvent: event->asPointerMouseEvent());
2636 // failsafe: never allow any kind of grab to persist after release
2637 if (event->isReleaseEvent() && event->buttons() == Qt::NoButton) {
2638 QQuickItem *oldGrabber = q->mouseGrabberItem();
2639 event->clearGrabbers();
2640 sendUngrabEvent(grabber: oldGrabber, touch: false);
2641 }
2642 } else if (event->asPointerTouchEvent()) {
2643 deliverTouchEvent(event->asPointerTouchEvent());
2644 } else {
2645 deliverSinglePointEventUntilAccepted(event);
2646 // If any handler got interested in the tablet event, we don't want to receive a synth-mouse event from QtGui
2647 // TODO Qt 6: QTabletEvent will be accepted by default, like other events
2648 if (event->asPointerTabletEvent() &&
2649 (!event->point(i: 0)->passiveGrabbers().isEmpty() || event->point(i: 0)->exclusiveGrabber()))
2650 event->setAccepted(true);
2651 }
2652
2653 event->reset(ev: nullptr);
2654
2655 --pointerEventRecursionGuard;
2656}
2657
2658// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
2659// FIXME: should this be iterative instead of recursive?
2660// 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.
2661// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
2662// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
2663QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const
2664{
2665 QVector<QQuickItem *> targets;
2666 auto itemPrivate = QQuickItemPrivate::get(item);
2667 QPointF itemPos = item->mapFromScene(point: point->scenePosition());
2668 // if the item clips, we can potentially return early
2669 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
2670 if (!item->contains(point: itemPos))
2671 return targets;
2672 }
2673
2674 // recurse for children
2675 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
2676 for (int ii = children.count() - 1; ii >= 0; --ii) {
2677 QQuickItem *child = children.at(i: ii);
2678 auto childPrivate = QQuickItemPrivate::get(item: child);
2679 if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
2680 continue;
2681 targets << pointerTargets(item: child, point, checkMouseButtons, checkAcceptsTouch);
2682 }
2683
2684 bool relevant = item->contains(point: itemPos);
2685 if (itemPrivate->hasPointerHandlers()) {
2686 if (!relevant)
2687 if (itemPrivate->anyPointerHandlerWants(point))
2688 relevant = true;
2689 } else {
2690 if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
2691 relevant = false;
2692 if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
2693 relevant = false;
2694 }
2695 if (relevant)
2696 targets << item; // add this item last: children take precedence
2697 return targets;
2698}
2699
2700// return the joined lists
2701// list1 has priority, common items come last
2702QVector<QQuickItem *> QQuickWindowPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
2703{
2704 QVector<QQuickItem *> targets = list1;
2705 // start at the end of list2
2706 // if item not in list, append it
2707 // if item found, move to next one, inserting before the last found one
2708 int insertPosition = targets.length();
2709 for (int i = list2.length() - 1; i >= 0; --i) {
2710 int newInsertPosition = targets.lastIndexOf(t: list2.at(i), from: insertPosition);
2711 if (newInsertPosition >= 0) {
2712 Q_ASSERT(newInsertPosition <= insertPosition);
2713 insertPosition = newInsertPosition;
2714 }
2715 // check for duplicates, only insert if the item isn't there already
2716 if (insertPosition == targets.size() || list2.at(i) != targets.at(i: insertPosition))
2717 targets.insert(i: insertPosition, t: list2.at(i));
2718 }
2719 return targets;
2720}
2721
2722void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event)
2723{
2724 qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent();
2725
2726 if (event->isPressEvent())
2727 deliverPressOrReleaseEvent(event);
2728 if (!event->allUpdatedPointsAccepted())
2729 deliverUpdatedTouchPoints(event);
2730 if (event->isReleaseEvent())
2731 deliverPressOrReleaseEvent(event, handlersOnly: true);
2732
2733 // Remove released points from itemForTouchPointId
2734 bool allReleased = true;
2735 int pointCount = event->pointCount();
2736 for (int i = 0; i < pointCount; ++i) {
2737 QQuickEventPoint *point = event->point(i);
2738 if (point->state() == QQuickEventPoint::Released) {
2739 int id = point->pointId();
2740 qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << id << "released";
2741 point->setGrabberItem(nullptr);
2742 if (id == touchMouseId)
2743 cancelTouchMouseSynthesis();
2744 } else {
2745 allReleased = false;
2746 }
2747 }
2748
2749 if (allReleased) {
2750 if (Q_UNLIKELY(!event->exclusiveGrabbers().isEmpty()))
2751 qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers();
2752 event->clearGrabbers();
2753 }
2754}
2755
2756// Deliver touch points to existing grabbers
2757void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event)
2758{
2759 bool done = false;
2760 const auto grabbers = event->exclusiveGrabbers();
2761 for (auto grabber : grabbers) {
2762 // The grabber is guaranteed to be either an item or a handler.
2763 QQuickItem *receiver = qmlobject_cast<QQuickItem *>(object: grabber);
2764 if (!receiver) {
2765 // The grabber is not an item? It's a handler then. Let it have the event first.
2766 QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
2767 receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
2768 hasFiltered.clear();
2769 if (sendFilteredPointerEvent(event, receiver))
2770 done = true;
2771 event->localize(target: receiver);
2772 handler->handlePointerEvent(event);
2773 }
2774 if (done)
2775 break;
2776 // If the grabber is an item or the grabbing handler didn't handle it,
2777 // then deliver the event to the item (which may have multiple handlers).
2778 deliverMatchingPointsToItem(item: receiver, pointerEvent: event);
2779 }
2780
2781 // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
2782 int pointCount = event->pointCount();
2783 for (int i = 0; i < pointCount; ++i) {
2784 QQuickEventPoint *point = event->point(i);
2785 deliverToPassiveGrabbers(passiveGrabbers: point->passiveGrabbers(), pointerEvent: event);
2786 }
2787
2788 if (done)
2789 return;
2790
2791 // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order
2792 if (!event->allPointsGrabbed()) {
2793 QVector<QQuickItem *> targetItems;
2794 for (int i = 0; i < pointCount; ++i) {
2795 QQuickEventPoint *point = event->point(i);
2796 if (point->state() == QQuickEventPoint::Pressed)
2797 continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
2798 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(item: contentItem, point, checkMouseButtons: false, checkAcceptsTouch: false);
2799 if (targetItems.count()) {
2800 targetItems = mergePointerTargets(list1: targetItems, list2: targetItemsForPoint);
2801 } else {
2802 targetItems = targetItemsForPoint;
2803 }
2804 }
2805 for (QQuickItem *item : targetItems) {
2806 if (grabbers.contains(t: item))
2807 continue;
2808 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2809 event->localize(target: item);
2810 itemPrivate->handlePointerEvent(event, avoidExclusiveGrabber: true); // avoid re-delivering to grabbers
2811 if (event->allPointsGrabbed())
2812 break;
2813 }
2814 }
2815}
2816
2817// Deliver an event containing newly pressed or released touch points
2818bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly)
2819{
2820 int pointCount = event->pointCount();
2821 QVector<QQuickItem *> targetItems;
2822 bool isTouchEvent = (event->asPointerTouchEvent() != nullptr);
2823 if (isTouchEvent && event->isPressEvent() && isDeliveringTouchAsMouse()) {
2824 if (const QQuickEventPoint *point = pointerEventInstance(device: touchMouseDevice)->pointById(pointId: touchMouseId)) {
2825 // When a second point is pressed, if the first point's existing
2826 // grabber was a pointer handler while a filtering parent is filtering
2827 // the same first point _as mouse_: we're starting over with delivery,
2828 // so we need to allow the second point to now be sent as a synth-mouse
2829 // instead of the first one, so that filtering parents (maybe even the
2830 // same one) can get a chance to see the second touchpoint as a
2831 // synth-mouse and perhaps grab it. Ideally we would always do this
2832 // when a new touchpoint is pressed, but this compromise fixes
2833 // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable
2834 if (point->grabberPointerHandler())
2835 cancelTouchMouseSynthesis();
2836 } else {
2837 qCWarning(DBG_TOUCH_TARGET) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event;
2838 }
2839 }
2840 for (int i = 0; i < pointCount; ++i) {
2841 auto point = event->point(i);
2842 if (point->state() == QQuickEventPoint::Pressed && !event->isDoubleClickEvent())
2843 point->clearPassiveGrabbers();
2844 point->setAccepted(false); // because otherwise touchEventForItem will ignore it
2845 if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
2846 point->setGrabberPointerHandler(exclusiveGrabber: nullptr, exclusive: true);
2847 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(item: contentItem, point, checkMouseButtons: !isTouchEvent, checkAcceptsTouch: isTouchEvent);
2848 if (targetItems.count()) {
2849 targetItems = mergePointerTargets(list1: targetItems, list2: targetItemsForPoint);
2850 } else {
2851 targetItems = targetItemsForPoint;
2852 }
2853 }
2854
2855 for (QQuickItem *item : targetItems) {
2856 if (!event->m_event) {
2857 qWarning(msg: "event went missing during delivery! (nested sendEvent() is not allowed)");
2858 break;
2859 }
2860 hasFiltered.clear();
2861 if (!handlersOnly && sendFilteredPointerEvent(event, receiver: item)) {
2862 if (event->isAccepted()) {
2863 for (int i = 0; i < event->pointCount(); ++i)
2864 event->point(i)->setAccepted();
2865 return true;
2866 }
2867 skipDelivery.append(t: item);
2868 }
2869
2870 // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event,
2871 // nor to any item which already had a chance to filter.
2872 if (skipDelivery.contains(t: item))
2873 continue;
2874 if (!event->m_event) {
2875 qWarning(msg: "event went missing during delivery! (nested sendEvent() is not allowed)");
2876 break;
2877 }
2878 deliverMatchingPointsToItem(item, pointerEvent: event, handlersOnly);
2879 if (event->allPointsAccepted())
2880 handlersOnly = true;
2881 }
2882
2883 return event->allPointsAccepted();
2884}
2885
2886void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly)
2887{
2888 Q_Q(QQuickWindow);
2889 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2890#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2891 // QTBUG-85379
2892 // In QT_VERSION below 6.0.0 touchEnabled for QtQuickItems is set by default to true
2893 // It causes delivering touch events to Items which are not interested
2894 // In some cases (like using Material Style in Android) it may cause a crash
2895 if (itemPrivate->wasDeleted)
2896 return;
2897#endif
2898 pointerEvent->localize(target: item);
2899
2900 // Let the Item's handlers (if any) have the event first.
2901 // However, double click should never be delivered to handlers.
2902 if (!pointerEvent->isDoubleClickEvent()) {
2903 bool wasAccepted = pointerEvent->allPointsAccepted();
2904 itemPrivate->handlePointerEvent(pointerEvent);
2905 allowDoubleClick = wasAccepted || !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted());
2906 }
2907 if (handlersOnly)
2908 return;
2909
2910 // If all points are released and the item is not the grabber, it doesn't get the event.
2911 // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
2912 if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent()
2913 && !pointerEvent->exclusiveGrabbers().contains(t: item))
2914 return;
2915
2916 // TODO: unite this mouse point delivery with the synthetic mouse event below
2917 auto event = pointerEvent->asPointerMouseEvent();
2918 if (event && item->acceptedMouseButtons() & event->button()) {
2919 auto point = event->point(i: 0);
2920 // The only reason to already have a mouse grabber here is
2921 // synthetic events - flickable sends one when setPressDelay is used.
2922 auto oldMouseGrabber = q->mouseGrabberItem();
2923 QPointF localPos = item->mapFromScene(point: point->scenePosition());
2924 QMouseEvent *me = event->asMouseEvent(localPos);
2925 me->accept();
2926 QCoreApplication::sendEvent(receiver: item, event: me);
2927 if (me->isAccepted()) {
2928 auto mouseGrabber = q->mouseGrabberItem();
2929 if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
2930 item->mouseUngrabEvent();
2931 } else if (item->isEnabled() && item->isVisible()) {
2932 item->grabMouse();
2933 }
2934 point->setAccepted(true);
2935 }
2936 return;
2937 }
2938
2939 QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent();
2940 if (!ptEvent)
2941 return;
2942
2943 QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item));
2944 if (!touchEvent)
2945 return;
2946
2947 qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item;
2948 bool eventAccepted = false;
2949
2950 // If any parent filters the event, we're done.
2951 hasFiltered.clear();
2952 if (sendFilteredPointerEvent(event: pointerEvent, receiver: item))
2953 return;
2954
2955 // Deliver the touch event to the given item
2956 qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item;
2957 QCoreApplication::sendEvent(receiver: item, event: touchEvent.data());
2958 eventAccepted = touchEvent->isAccepted();
2959
2960 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
2961 if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
2962 if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
2963 // send mouse event
2964 if (deliverTouchAsMouse(item, pointerEvent: ptEvent))
2965 eventAccepted = true;
2966 }
2967 }
2968
2969 if (eventAccepted) {
2970 // If the touch was accepted (regardless by whom or in what form),
2971 // update accepted new points.
2972 bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent();
2973 for (const auto &point: qAsConst(t: touchEvent->touchPoints())) {
2974 if (auto pointerEventPoint = ptEvent->pointById(pointId: point.id())) {
2975 pointerEventPoint->setAccepted();
2976 if (isPressOrRelease)
2977 pointerEventPoint->setGrabberItem(item);
2978 }
2979 }
2980 } else {
2981 // But if the event was not accepted then we know this item
2982 // will not be interested in further updates for those touchpoint IDs either.
2983 for (const auto &point: qAsConst(t: touchEvent->touchPoints())) {
2984 if (point.state() == Qt::TouchPointPressed) {
2985 if (auto *tp = ptEvent->pointById(pointId: point.id())) {
2986 if (tp->exclusiveGrabber() == item) {
2987 qCDebug(DBG_TOUCH_TARGET) << "TP" << Qt::hex << point.id() << "disassociated";
2988 tp->setGrabberItem(nullptr);
2989 }
2990 }
2991 }
2992 }
2993 }
2994}
2995
2996#if QT_CONFIG(quick_draganddrop)
2997void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
2998{
2999 grabber->resetTarget();
3000 QQuickDragGrabber::iterator grabItem = grabber->begin();
3001 if (grabItem != grabber->end()) {
3002 Q_ASSERT(event->type() != QEvent::DragEnter);
3003 if (event->type() == QEvent::Drop) {
3004 QDropEvent *e = static_cast<QDropEvent *>(event);
3005 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(at: grabItem)) {
3006 QPointF p = (**grabItem)->mapFromScene(point: e->pos());
3007 QDropEvent translatedEvent(
3008 p.toPoint(),
3009 e->possibleActions(),
3010 e->mimeData(),
3011 e->mouseButtons(),
3012 e->keyboardModifiers());
3013 QQuickDropEventEx::copyActions(to: &translatedEvent, from: *e);
3014 QCoreApplication::sendEvent(receiver: **grabItem, event: &translatedEvent);
3015 e->setAccepted(translatedEvent.isAccepted());
3016 e->setDropAction(translatedEvent.dropAction());
3017 grabber->setTarget(**grabItem);
3018 }
3019 }
3020 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
3021 QDragLeaveEvent leaveEvent;
3022 for (; grabItem != grabber->end(); grabItem = grabber->release(at: grabItem))
3023 QCoreApplication::sendEvent(receiver: **grabItem, event: &leaveEvent);
3024 return;
3025 } else {
3026 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
3027
3028 // Used to ensure we don't send DragEnterEvents to current drop targets,
3029 // and to detect which current drop targets we have left
3030 QVarLengthArray<QQuickItem*, 64> currentGrabItems;
3031 for (; grabItem != grabber->end(); grabItem = grabber->release(at: grabItem))
3032 currentGrabItems.append(t: **grabItem);
3033
3034 // Look for any other potential drop targets that are higher than the current ones
3035 QDragEnterEvent enterEvent(
3036 moveEvent->pos(),
3037 moveEvent->possibleActions(),
3038 moveEvent->mimeData(),
3039 moveEvent->mouseButtons(),
3040 moveEvent->keyboardModifiers());
3041 QQuickDropEventEx::copyActions(to: &enterEvent, from: *moveEvent);
3042 event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent, currentGrabItems: &currentGrabItems));
3043
3044 for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
3045 int i = currentGrabItems.indexOf(t: **grabItem);
3046 if (i >= 0) {
3047 currentGrabItems.remove(i);
3048 // Still grabbed: send move event
3049 QDragMoveEvent translatedEvent(
3050 (**grabItem)->mapFromScene(point: moveEvent->pos()).toPoint(),
3051 moveEvent->possibleActions(),
3052 moveEvent->mimeData(),
3053 moveEvent->mouseButtons(),
3054 moveEvent->keyboardModifiers());
3055 QQuickDropEventEx::copyActions(to: &translatedEvent, from: *moveEvent);
3056 QCoreApplication::sendEvent(receiver: **grabItem, event: &translatedEvent);
3057 event->setAccepted(translatedEvent.isAccepted());
3058 QQuickDropEventEx::copyActions(to: moveEvent, from: translatedEvent);
3059 }
3060 }
3061
3062 // Anything left in currentGrabItems is no longer a drop target and should be sent a DragLeaveEvent
3063 QDragLeaveEvent leaveEvent;
3064 for (QQuickItem *i : currentGrabItems)
3065 QCoreApplication::sendEvent(receiver: i, event: &leaveEvent);
3066
3067 return;
3068 }
3069 }
3070 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
3071 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
3072 QDragEnterEvent enterEvent(
3073 e->pos(),
3074 e->possibleActions(),
3075 e->mimeData(),
3076 e->mouseButtons(),
3077 e->keyboardModifiers());
3078 QQuickDropEventEx::copyActions(to: &enterEvent, from: *e);
3079 event->setAccepted(deliverDragEvent(grabber, contentItem, &enterEvent));
3080 }
3081}
3082
3083bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event, QVarLengthArray<QQuickItem*, 64> *currentGrabItems)
3084{
3085 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
3086 if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
3087 return false;
3088 QPointF p = item->mapFromScene(point: event->pos());
3089 bool itemContained = item->contains(point: p);
3090
3091 if (!itemContained && itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
3092 return false;
3093 }
3094
3095 QDragEnterEvent enterEvent(
3096 event->pos(),
3097 event->possibleActions(),
3098 event->mimeData(),
3099 event->mouseButtons(),
3100 event->keyboardModifiers());
3101 QQuickDropEventEx::copyActions(to: &enterEvent, from: *event);
3102 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
3103
3104 // Check children in front of this item first
3105 for (int ii = children.count() - 1; ii >= 0; --ii) {
3106 if (children.at(i: ii)->z() < 0)
3107 continue;
3108 if (deliverDragEvent(grabber, item: children.at(i: ii), event: &enterEvent, currentGrabItems))
3109 return true;
3110 }
3111
3112 if (itemContained) {
3113 // If this item is currently grabbed, don't send it another DragEnter,
3114 // just grab it again if it's still contained.
3115 if (currentGrabItems && currentGrabItems->contains(t: item)) {
3116 grabber->grab(item);
3117 grabber->setTarget(item);
3118 return true;
3119 }
3120
3121 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
3122 QDragMoveEvent translatedEvent(
3123 p.toPoint(),
3124 event->possibleActions(),
3125 event->mimeData(),
3126 event->mouseButtons(),
3127 event->keyboardModifiers(),
3128 event->type());
3129 QQuickDropEventEx::copyActions(to: &translatedEvent, from: *event);
3130 translatedEvent.setAccepted(event->isAccepted());
3131 QCoreApplication::sendEvent(receiver: item, event: &translatedEvent);
3132 event->setAccepted(translatedEvent.isAccepted());
3133 event->setDropAction(translatedEvent.dropAction());
3134 if (event->type() == QEvent::DragEnter) {
3135 if (translatedEvent.isAccepted()) {
3136 grabber->grab(item);
3137 grabber->setTarget(item);
3138 return true;
3139 }
3140 } else {
3141 return true;
3142 }
3143 }
3144 }
3145
3146 // Check children behind this item if this item or any higher children have not accepted
3147 for (int ii = children.count() - 1; ii >= 0; --ii) {
3148 if (children.at(i: ii)->z() >= 0)
3149 continue;
3150 if (deliverDragEvent(grabber, item: children.at(i: ii), event: &enterEvent, currentGrabItems))
3151 return true;
3152 }
3153
3154 return false;
3155}
3156#endif // quick_draganddrop
3157
3158#if QT_CONFIG(cursor)
3159void QQuickWindowPrivate::updateCursor(const QPointF &scenePos)
3160{
3161 Q_Q(QQuickWindow);
3162
3163 auto cursorItemAndHandler = findCursorItemAndHandler(item: contentItem, scenePos);
3164
3165 if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) {
3166 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: q);
3167 QWindow *window = renderWindow ? renderWindow : q;
3168 cursorItem = cursorItemAndHandler.first;
3169 cursorHandler = cursorItemAndHandler.second;
3170 if (cursorItem)
3171 window->setCursor(QQuickItemPrivate::get(item: cursorItem)->effectiveCursor(handler: cursorHandler));
3172 else
3173 window->unsetCursor();
3174 }
3175}
3176
3177QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const
3178{
3179 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
3180 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
3181 QPointF p = item->mapFromScene(point: scenePos);
3182 if (!item->contains(point: p))
3183 return {nullptr, nullptr};
3184 }
3185
3186 if (itemPrivate->subtreeCursorEnabled) {
3187 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
3188 for (int ii = children.count() - 1; ii >= 0; --ii) {
3189 QQuickItem *child = children.at(i: ii);
3190 if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled)
3191 continue;
3192 auto ret = findCursorItemAndHandler(item: child, scenePos);
3193 if (ret.first)
3194 return ret;
3195 }
3196 if (itemPrivate->hasCursorHandler) {
3197 if (auto handler = itemPrivate->effectiveCursorHandler()) {
3198 QQuickPointerEvent *pointerEvent = pointerEventInstance(device: QQuickPointerDevice::genericMouseDevice(), eventType: QEvent::MouseMove);
3199 pointerEvent->point(i: 0)->reset(state: Qt::TouchPointMoved, scenePosition: scenePos, pointId: quint64(1) << 24 /* mouse has device ID 1 */, timestamp: 0);
3200 pointerEvent->point(i: 0)->setAccepted(true);
3201 pointerEvent->localize(target: item);
3202 if (handler->parentContains(point: pointerEvent->point(i: 0)))
3203 return {item, handler};
3204 }
3205 }
3206 if (itemPrivate->hasCursor) {
3207 QPointF p = item->mapFromScene(point: scenePos);
3208 if (item->contains(point: p))
3209 return {item, nullptr};
3210 }
3211 }
3212
3213 return {nullptr, nullptr};
3214}
3215#endif
3216
3217bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
3218{
3219 return sendFilteredPointerEventImpl(event, receiver, filteringParent: filteringParent ? filteringParent : receiver->parentItem());
3220}
3221
3222bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
3223{
3224 if (!allowChildEventFiltering)
3225 return false;
3226 if (!filteringParent)
3227 return false;
3228 bool filtered = false;
3229 if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(t: filteringParent)) {
3230 hasFiltered.append(t: filteringParent);
3231 if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) {
3232 if (receiver->acceptedMouseButtons()) {
3233 QPointF localPos = receiver->mapFromScene(point: pme->point(i: 0)->scenePosition());
3234 QMouseEvent *me = pme->asMouseEvent(localPos);
3235 const bool wasAccepted = me->isAccepted();
3236 me->setAccepted(true);
3237 auto oldMouseGrabber = pme->point(i: 0)->grabberItem();
3238 if (filteringParent->childMouseEventFilter(receiver, me)) {
3239 qCDebug(DBG_MOUSE) << "mouse event intercepted by childMouseEventFilter of " << filteringParent;
3240 skipDelivery.append(t: filteringParent);
3241 filtered = true;
3242 if (me->isAccepted() && pme->isPressEvent()) {
3243 auto mouseGrabber = pme->point(i: 0)->grabberItem();
3244 if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) {
3245 receiver->mouseUngrabEvent();
3246 } else {
3247 pme->point(i: 0)->setGrabberItem(receiver);
3248 }
3249 }
3250 } else {
3251 // Restore accepted state if the event was not filtered.
3252 me->setAccepted(wasAccepted);
3253 }
3254 }
3255 } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) {
3256#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
3257 bool acceptsTouchEvents = receiver->acceptTouchEvents();
3258#else
3259 // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true.
3260 bool acceptsTouchEvents = false;
3261#endif
3262 auto device = pte->device();
3263 if (device->type() == QQuickPointerDevice::TouchPad &&
3264 device->capabilities().testFlag(flag: QQuickPointerDevice::MouseEmulation)) {
3265 qCDebug(DBG_TOUCH_TARGET) << "skipping filtering of synth-mouse event from" << device;
3266 } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) {
3267 // get a touch event customized for delivery to filteringParent
3268 QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(item: receiver, isFiltering: true));
3269 if (filteringParentTouchEvent) {
3270 if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) {
3271 qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
3272 skipDelivery.append(t: filteringParent);
3273 for (const auto &point: qAsConst(t: filteringParentTouchEvent->touchPoints())) {
3274 QQuickEventPoint *pt = event->pointById(pointId: point.id());
3275 pt->setAccepted();
3276 pt->setGrabberItem(filteringParent);
3277 }
3278 return true;
3279 } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
3280 // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
3281 for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) {
3282 const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i);
3283
3284 QEvent::Type t;
3285 switch (tp.state()) {
3286 case Qt::TouchPointPressed:
3287 t = QEvent::MouseButtonPress;
3288 break;
3289 case Qt::TouchPointReleased:
3290 t = QEvent::MouseButtonRelease;
3291 break;
3292 case Qt::TouchPointStationary:
3293 continue;
3294 default:
3295 t = QEvent::MouseMove;
3296 break;
3297 }
3298
3299 bool touchMouseUnset = (touchMouseId == -1);
3300 // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
3301 if (touchMouseUnset || touchMouseId == tp.id()) {
3302 // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.)
3303 // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that
3304 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(type: t, p: tp, event: filteringParentTouchEvent.data(), item: receiver, transformNeeded: false));
3305 // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
3306 // report the touchpoint's grabber. Whenever we send a synthetic mouse event,
3307 // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
3308 touchMouseId = tp.id();
3309 touchMouseDevice = event->device();
3310 QQuickPointerDevice *dev = touchMouseDevice;
3311 if (filteringParent->childMouseEventFilter(receiver, mouseEvent.data())) {
3312 qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
3313 skipDelivery.append(t: filteringParent);
3314 if (t != QEvent::MouseButtonRelease) {
3315 qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent;
3316 pointerEventInstance(device: dev)->pointById(pointId: tp.id())->setGrabberItem(filteringParent);
3317 touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
3318 if (mouseEvent->isAccepted())
3319 filteringParent->grabMouse();
3320 }
3321 filtered = true;
3322 }
3323 if (touchMouseUnset) {
3324 // Now that we're done sending a synth mouse event, and it wasn't grabbed,
3325 // the touchpoint is no longer acting as a synthetic mouse. Restore previous state.
3326 cancelTouchMouseSynthesis();
3327 }
3328 // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter
3329 // has been called once, we're done with this loop over the touchpoints.
3330 break;
3331 }
3332 }
3333 }
3334 }
3335 }
3336 }
3337 }
3338 return sendFilteredPointerEventImpl(event, receiver, filteringParent: filteringParent->parentItem()) || filtered;
3339}
3340
3341bool QQuickWindowPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
3342{
3343 if (!filteringParent)
3344 return false;
3345
3346 QQuickItemPrivate *filteringParentPrivate = QQuickItemPrivate::get(item: filteringParent);
3347 if (filteringParentPrivate->replayingPressEvent)
3348 return false;
3349
3350 bool filtered = false;
3351 if (filteringParentPrivate->filtersChildMouseEvents && !hasFiltered.contains(t: filteringParent)) {
3352 hasFiltered.append(t: filteringParent);
3353 if (filteringParent->childMouseEventFilter(receiver, event)) {
3354 filtered = true;
3355 skipDelivery.append(t: filteringParent);
3356 }
3357 qCDebug(DBG_MOUSE_TARGET) << "for" << receiver << filteringParent << "childMouseEventFilter ->" << filtered;
3358 }
3359
3360 return sendFilteredMouseEvent(event, receiver, filteringParent: filteringParent->parentItem()) || filtered;
3361}
3362
3363bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold)
3364{
3365 QStyleHints *styleHints = QGuiApplication::styleHints();
3366 int caps = QGuiApplicationPrivate::mouseEventCaps(event);
3367 bool dragVelocityLimitAvailable = (caps & QTouchDevice::Velocity)
3368 && styleHints->startDragVelocity();
3369 bool overThreshold = qAbs(t: d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
3370 if (dragVelocityLimitAvailable) {
3371 QVector2D velocityVec = QGuiApplicationPrivate::mouseEventVelocity(event);
3372 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
3373 overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity();
3374 }
3375 return overThreshold;
3376}
3377
3378bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold)
3379{
3380 QStyleHints *styleHints = qApp->styleHints();
3381 bool overThreshold = qAbs(t: d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
3382 const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
3383 if (!overThreshold && dragVelocityLimitAvailable) {
3384 qreal velocity = axis == Qt::XAxis ? tp->velocity().x() : tp->velocity().y();
3385 overThreshold |= qAbs(t: velocity) > styleHints->startDragVelocity();
3386 }
3387 return overThreshold;
3388}
3389
3390bool QQuickWindowPrivate::dragOverThreshold(QVector2D delta)
3391{
3392 int threshold = qApp->styleHints()->startDragDistance();
3393 return qAbs(t: delta.x()) > threshold || qAbs(t: delta.y()) > threshold;
3394}
3395
3396/*!
3397 \qmlproperty list<Object> Window::data
3398 \default
3399
3400 The data property allows you to freely mix visual children, resources
3401 and other Windows in a Window.
3402
3403 If you assign another Window to the data list, the nested window will
3404 become "transient for" the outer Window.
3405
3406 If you assign an \l Item to the data list, it becomes a child of the
3407 Window's \l contentItem, so that it appears inside the window. The item's
3408 parent will be the window's contentItem, which is the root of the Item
3409 ownership tree within that Window.
3410
3411 If you assign any other object type, it is added as a resource.
3412
3413 It should not generally be necessary to refer to the \c data property,
3414 as it is the default property for Window and thus all child items are
3415 automatically assigned to this property.
3416
3417 \sa QWindow::transientParent()
3418 */
3419
3420void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObject *o)
3421{
3422 if (!o)
3423 return;
3424 QQuickWindow *that = static_cast<QQuickWindow *>(property->object);
3425 if (QQuickWindow *window = qmlobject_cast<QQuickWindow *>(object: o)) {
3426 qCDebug(lcTransient) << window << "is transient for" << that;
3427 window->setTransientParent(that);
3428 }
3429 QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: that->contentItem())->data();
3430 itemProperty.append(&itemProperty, o);
3431}
3432
3433int QQuickWindowPrivate::data_count(QQmlListProperty<QObject> *property)
3434{
3435 QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
3436 if (!win || !win->contentItem() || !QQuickItemPrivate::get(item: win->contentItem())->data().count)
3437 return 0;
3438 QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data();
3439 return itemProperty.count(&itemProperty);
3440}
3441
3442QObject *QQuickWindowPrivate::data_at(QQmlListProperty<QObject> *property, int i)
3443{
3444 QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
3445 QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data();
3446 return itemProperty.at(&itemProperty, i);
3447}
3448
3449void QQuickWindowPrivate::data_clear(QQmlListProperty<QObject> *property)
3450{
3451 QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
3452 QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data();
3453 itemProperty.clear(&itemProperty);
3454}
3455
3456void QQuickWindowPrivate::data_replace(QQmlListProperty<QObject> *property, int i, QObject *o)
3457{
3458 QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
3459 QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data();
3460 itemProperty.replace(&itemProperty, i, o);
3461}
3462
3463void QQuickWindowPrivate::data_removeLast(QQmlListProperty<QObject> *property)
3464{
3465 QQuickWindow *win = static_cast<QQuickWindow*>(property->object);
3466 QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data();
3467 itemProperty.removeLast(&itemProperty);
3468}
3469
3470bool QQuickWindowPrivate::isRenderable() const
3471{
3472 Q_Q(const QQuickWindow);
3473 return ((q->isExposed() && q->isVisible())) && q->geometry().isValid();
3474}
3475
3476void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &format,
3477 QString *translatedMessage,
3478 QString *untranslatedMessage)
3479{
3480 const QString contextType = QLatin1String("OpenGL");
3481 QString formatStr;
3482 QDebug(&formatStr) << format;
3483#if defined(Q_OS_WIN32)
3484 const bool isDebug = QLibraryInfo::isDebugBuild();
3485 const QString eglLibName = QLatin1String(isDebug ? "libEGLd.dll" : "libEGL.dll");
3486 const QString glesLibName = QLatin1String(isDebug ? "libGLESv2d.dll" : "libGLESv2.dll");
3487 //: %1 Context type (Open GL, EGL), %2 format, ANGLE %3, %4 library names
3488 const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow",
3489 "Failed to create %1 context for format %2.\n"
3490 "This is most likely caused by not having the necessary graphics drivers installed.\n\n"
3491 "Install a driver providing OpenGL 2.0 or higher, or, if this is not possible, "
3492 "make sure the ANGLE Open GL ES 2.0 emulation libraries (%3, %4 and d3dcompiler_*.dll) "
3493 "are available in the application executable's directory or in a location listed in PATH.");
3494 *translatedMessage = QQuickWindow::tr(msg).arg(contextType, formatStr, eglLibName, glesLibName);
3495 *untranslatedMessage = QString::fromLatin1(msg).arg(contextType, formatStr, eglLibName, glesLibName);
3496#else // Q_OS_WIN32
3497 //: %1 Context type (Open GL, EGL), %2 format specification
3498 const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow",
3499 "Failed to create %1 context for format %2");
3500 *translatedMessage = QQuickWindow::tr(s: msg).arg(args: contextType, args&: formatStr);
3501 *untranslatedMessage = QString::fromLatin1(str: msg).arg(args: contextType, args&: formatStr);
3502#endif // !Q_OS_WIN32
3503}
3504
3505void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName,
3506 QString *translatedMessage,
3507 QString *untranslatedMessage)
3508{
3509 const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow",
3510 "Failed to initialize graphics backend for %1.");
3511 *translatedMessage = QQuickWindow::tr(s: msg).arg(a: backendName);
3512 *untranslatedMessage = QString::fromLatin1(str: msg).arg(a: backendName);
3513}
3514
3515#if QT_DEPRECATED_SINCE(5, 8)
3516
3517// ### Qt6: remove
3518/*!
3519 Propagates an event \a e to a QQuickItem \a item on the window.
3520
3521 Use \l QCoreApplication::sendEvent() directly instead.
3522
3523 The return value is currently not used.
3524
3525 \deprecated
3526*/
3527bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e)
3528{
3529 Q_D(QQuickWindow);
3530
3531 if (!item) {
3532 qWarning(msg: "QQuickWindow::sendEvent: Cannot send event to a null item");
3533 return false;
3534 }
3535
3536 Q_ASSERT(e);
3537
3538 switch (e->type()) {
3539 case QEvent::KeyPress:
3540 case QEvent::KeyRelease:
3541 e->accept();
3542 QCoreApplication::sendEvent(receiver: item, event: e);
3543 while (!e->isAccepted() && (item = item->parentItem())) {
3544 e->accept();
3545 QCoreApplication::sendEvent(receiver: item, event: e);
3546 }
3547 break;
3548 case QEvent::MouseButtonPress:
3549 case QEvent::MouseButtonRelease:
3550 case QEvent::MouseButtonDblClick:
3551 case QEvent::MouseMove: {
3552 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
3553 d->hasFiltered.clear();
3554 if (!d->sendFilteredMouseEvent(event: e, receiver: item, filteringParent: item->parentItem())) {
3555 // accept because qml items by default accept and have to explicitly opt out of accepting
3556 e->accept();
3557 QCoreApplication::sendEvent(receiver: item, event: e);
3558 }
3559 }
3560 break;
3561 default:
3562 QCoreApplication::sendEvent(receiver: item, event: e);
3563 break;
3564 }
3565
3566 return false;
3567}
3568
3569#endif
3570
3571void QQuickWindowPrivate::cleanupNodes()
3572{
3573 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
3574 delete cleanupNodeList.at(i: ii);
3575 cleanupNodeList.clear();
3576}
3577
3578void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item)
3579{
3580 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
3581 if (p->itemNodeInstance) {
3582 delete p->itemNodeInstance;
3583 p->itemNodeInstance = nullptr;
3584
3585 if (p->extra.isAllocated()) {
3586 p->extra->opacityNode = nullptr;
3587 p->extra->clipNode = nullptr;
3588 p->extra->rootNode = nullptr;
3589 }
3590
3591 p->paintNode = nullptr;
3592
3593 p->dirty(QQuickItemPrivate::Window);
3594 }
3595
3596 // Qt 6: Make invalidateSceneGraph a virtual member of QQuickItem
3597 if (p->flags & QQuickItem::ItemHasContents) {
3598 const QMetaObject *mo = item->metaObject();
3599 int index = mo->indexOfSlot(slot: "invalidateSceneGraph()");
3600 if (index >= 0) {
3601 const QMetaMethod &method = mo->method(index);
3602 // Skip functions named invalidateSceneGraph() in QML items.
3603 if (strstr(haystack: method.enclosingMetaObject()->className(), needle: "_QML_") == nullptr)
3604 method.invoke(object: item, connectionType: Qt::DirectConnection);
3605 }
3606 }
3607
3608 for (int ii = 0; ii < p->childItems.count(); ++ii)
3609 cleanupNodesOnShutdown(item: p->childItems.at(i: ii));
3610}
3611
3612// This must be called from the render thread, with the main thread frozen
3613void QQuickWindowPrivate::cleanupNodesOnShutdown()
3614{
3615 Q_Q(QQuickWindow);
3616 cleanupNodes();
3617 cleanupNodesOnShutdown(item: contentItem);
3618 for (QSet<QQuickItem *>::const_iterator it = parentlessItems.begin(), cend = parentlessItems.end(); it != cend; ++it)
3619 cleanupNodesOnShutdown(item: *it);
3620 animationController->windowNodesDestroyed();
3621 q->cleanupSceneGraph();
3622}
3623
3624void QQuickWindowPrivate::updateDirtyNodes()
3625{
3626 qCDebug(DBG_DIRTY) << "QQuickWindowPrivate::updateDirtyNodes():";
3627
3628 cleanupNodes();
3629
3630 QQuickItem *updateList = dirtyItemList;
3631 dirtyItemList = nullptr;
3632 if (updateList) QQuickItemPrivate::get(item: updateList)->prevDirtyItem = &updateList;
3633
3634 while (updateList) {
3635 QQuickItem *item = updateList;
3636 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
3637 itemPriv->removeFromDirtyList();
3638
3639 qCDebug(DBG_DIRTY) << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
3640 updateDirtyNode(item);
3641 }
3642}
3643
3644static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d)
3645{
3646 const QList<QQuickItem *> childItems = d->paintOrderChildItems();
3647 QQuickItem *before = nullptr;
3648 for (int i=0; i<childItems.size(); ++i) {
3649 QQuickItemPrivate *dd = QQuickItemPrivate::get(item: childItems.at(i));
3650 // Perform the same check as the in fetchNextNode below.
3651 if (dd->z() < 0 && (dd->explicitVisible || (dd->extra.isAllocated() && dd->extra->effectRefCount)))
3652 before = childItems.at(i);
3653 else
3654 break;
3655 }
3656 return Q_UNLIKELY(before) ? QQuickItemPrivate::get(item: before)->itemNode() : nullptr;
3657}
3658
3659static QSGNode *fetchNextNode(QQuickItemPrivate *itemPriv, int &ii, bool &returnedPaintNode)
3660{
3661 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
3662
3663 for (; ii < orderedChildren.count() && orderedChildren.at(i: ii)->z() < 0; ++ii) {
3664 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii));
3665 if (!childPrivate->explicitVisible &&
3666 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
3667 continue;
3668
3669 ii++;
3670 return childPrivate->itemNode();
3671 }
3672
3673 if (itemPriv->paintNode && !returnedPaintNode) {
3674 returnedPaintNode = true;
3675 return itemPriv->paintNode;
3676 }
3677
3678 for (; ii < orderedChildren.count(); ++ii) {
3679 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii));
3680 if (!childPrivate->explicitVisible &&
3681 (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount))
3682 continue;
3683
3684 ii++;
3685 return childPrivate->itemNode();
3686 }
3687
3688 return nullptr;
3689}
3690
3691void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item)
3692{
3693 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
3694 quint32 dirty = itemPriv->dirtyAttributes;
3695 itemPriv->dirtyAttributes = 0;
3696
3697 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
3698 (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft &&
3699 (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) {
3700
3701 QMatrix4x4 matrix;
3702
3703 if (itemPriv->x != 0. || itemPriv->y != 0.)
3704 matrix.translate(x: itemPriv->x, y: itemPriv->y);
3705
3706 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
3707 itemPriv->transforms.at(i: ii)->applyTo(matrix: &matrix);
3708
3709 if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) {
3710 QPointF origin = item->transformOriginPoint();
3711 matrix.translate(x: origin.x(), y: origin.y());
3712 if (itemPriv->scale() != 1.)
3713 matrix.scale(x: itemPriv->scale(), y: itemPriv->scale());
3714 if (itemPriv->rotation() != 0.)
3715 matrix.rotate(angle: itemPriv->rotation(), x: 0, y: 0, z: 1);
3716 matrix.translate(x: -origin.x(), y: -origin.y());
3717 }
3718
3719 itemPriv->itemNode()->setMatrix(matrix);
3720 }
3721
3722 bool clipEffectivelyChanged = (dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window)) &&
3723 ((item->clip() == false) != (itemPriv->clipNode() == nullptr));
3724 int effectRefCount = itemPriv->extra.isAllocated()?itemPriv->extra->effectRefCount:0;
3725 bool effectRefEffectivelyChanged = (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) &&
3726 ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr));
3727
3728 if (clipEffectivelyChanged) {
3729 QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *) itemPriv->opacityNode() :
3730 (QSGNode *) itemPriv->itemNode();
3731 QSGNode *child = itemPriv->rootNode();
3732
3733 if (item->clip()) {
3734 Q_ASSERT(itemPriv->clipNode() == nullptr);
3735 QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect());
3736 itemPriv->extra.value().clipNode = clip;
3737 clip->update();
3738
3739 if (!child) {
3740 parent->reparentChildNodesTo(newParent: clip);
3741 parent->appendChildNode(node: clip);
3742 } else {
3743 parent->removeChildNode(node: child);
3744 clip->appendChildNode(node: child);
3745 parent->appendChildNode(node: clip);
3746 }
3747
3748 } else {
3749 QQuickDefaultClipNode *clip = itemPriv->clipNode();
3750 Q_ASSERT(clip);
3751 parent->removeChildNode(node: clip);
3752 if (child) {
3753 clip->removeChildNode(node: child);
3754 parent->appendChildNode(node: child);
3755 } else {
3756 clip->reparentChildNodesTo(newParent: parent);
3757 }
3758
3759 delete itemPriv->clipNode();
3760 itemPriv->extra->clipNode = nullptr;
3761 }
3762 }
3763
3764 if (effectRefEffectivelyChanged) {
3765 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
3766 itemPriv->childContainerNode()->removeAllChildNodes();
3767
3768 QSGNode *parent = itemPriv->clipNode();
3769 if (!parent)
3770 parent = itemPriv->opacityNode();
3771 if (!parent)
3772 parent = itemPriv->itemNode();
3773
3774 if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) {
3775 Q_ASSERT(itemPriv->rootNode() == nullptr);
3776 QSGRootNode *root = new QSGRootNode();
3777 itemPriv->extra->rootNode = root;
3778 parent->reparentChildNodesTo(newParent: root);
3779 parent->appendChildNode(node: root);
3780 } else {
3781 Q_ASSERT(itemPriv->rootNode() != nullptr);
3782 QSGRootNode *root = itemPriv->rootNode();
3783 parent->removeChildNode(node: root);
3784 root->reparentChildNodesTo(newParent: parent);
3785 delete itemPriv->rootNode();
3786 itemPriv->extra->rootNode = nullptr;
3787 }
3788 }
3789
3790 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
3791 int ii = 0;
3792 bool fetchedPaintNode = false;
3793 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
3794 int desiredNodesSize = orderedChildren.size() + (itemPriv->paintNode ? 1 : 0);
3795
3796 // now start making current state match the promised land of
3797 // desiredNodes. in the case of our current state matching desiredNodes
3798 // (though why would we get ChildrenUpdateMask with no changes?) then we
3799 // should make no changes at all.
3800
3801 // how many nodes did we process, when examining changes
3802 int desiredNodesProcessed = 0;
3803
3804 // currentNode is how far, in our present tree, we have processed. we
3805 // make use of this later on to trim the current child list if the
3806 // desired list is shorter.
3807 QSGNode *groupNode = itemPriv->childContainerNode();
3808 QSGNode *currentNode = groupNode->firstChild();
3809 int added = 0;
3810 int removed = 0;
3811 int replaced = 0;
3812 QSGNode *desiredNode = nullptr;
3813
3814 while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) {
3815 // uh oh... reality and our utopic paradise are diverging!
3816 // we need to reconcile this...
3817 if (currentNode != desiredNode) {
3818 // for now, we're just removing the node from the children -
3819 // and replacing it with the new node.
3820 if (desiredNode->parent())
3821 desiredNode->parent()->removeChildNode(node: desiredNode);
3822 groupNode->insertChildNodeAfter(node: desiredNode, after: currentNode);
3823 groupNode->removeChildNode(node: currentNode);
3824 replaced++;
3825
3826 // since we just replaced currentNode, we also need to reset
3827 // the pointer.
3828 currentNode = desiredNode;
3829 }
3830
3831 currentNode = currentNode->nextSibling();
3832 desiredNodesProcessed++;
3833 }
3834
3835 // if we didn't process as many nodes as in the new list, then we have
3836 // more nodes at the end of desiredNodes to append to our list.
3837 // this will be the case when adding new nodes, for instance.
3838 if (desiredNodesProcessed < desiredNodesSize) {
3839 while ((desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) {
3840 if (desiredNode->parent())
3841 desiredNode->parent()->removeChildNode(node: desiredNode);
3842 groupNode->appendChildNode(node: desiredNode);
3843 added++;
3844 }
3845 } else if (currentNode) {
3846 // on the other hand, if we processed less than our current node
3847 // tree, then nodes have been _removed_ from the scene, and we need
3848 // to take care of that here.
3849 while (currentNode) {
3850 QSGNode *node = currentNode->nextSibling();
3851 groupNode->removeChildNode(node: currentNode);
3852 currentNode = node;
3853 removed++;
3854 }
3855 }
3856 }
3857
3858 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) {
3859 itemPriv->clipNode()->setRect(item->clipRect());
3860 itemPriv->clipNode()->update();
3861 }
3862
3863 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible
3864 | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window))
3865 {
3866 qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0)
3867 ? itemPriv->opacity() : qreal(0);
3868
3869 if (opacity != 1 && !itemPriv->opacityNode()) {
3870 QSGOpacityNode *node = new QSGOpacityNode;
3871 itemPriv->extra.value().opacityNode = node;
3872
3873 QSGNode *parent = itemPriv->itemNode();
3874 QSGNode *child = itemPriv->clipNode();
3875 if (!child)
3876 child = itemPriv->rootNode();
3877
3878 if (child) {
3879 parent->removeChildNode(node: child);
3880 node->appendChildNode(node: child);
3881 parent->appendChildNode(node);
3882 } else {
3883 parent->reparentChildNodesTo(newParent: node);
3884 parent->appendChildNode(node);
3885 }
3886 }
3887 if (itemPriv->opacityNode())
3888 itemPriv->opacityNode()->setOpacity(opacity);
3889 }
3890
3891 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
3892
3893 if (itemPriv->flags & QQuickItem::ItemHasContents) {
3894 updatePaintNodeData.transformNode = itemPriv->itemNode();
3895 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
3896
3897 Q_ASSERT(itemPriv->paintNode == nullptr ||
3898 itemPriv->paintNode->parent() == nullptr ||
3899 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
3900
3901 if (itemPriv->paintNode && itemPriv->paintNode->parent() == nullptr) {
3902 QSGNode *before = qquickitem_before_paintNode(d: itemPriv);
3903 if (before && before->parent()) {
3904 Q_ASSERT(before->parent() == itemPriv->childContainerNode());
3905 itemPriv->childContainerNode()->insertChildNodeAfter(node: itemPriv->paintNode, after: before);
3906 } else {
3907 itemPriv->childContainerNode()->prependChildNode(node: itemPriv->paintNode);
3908 }
3909 }
3910 } else if (itemPriv->paintNode) {
3911 delete itemPriv->paintNode;
3912 itemPriv->paintNode = nullptr;
3913 }
3914 }
3915
3916#ifndef QT_NO_DEBUG
3917 // Check consistency.
3918
3919 QList<QSGNode *> nodes;
3920 nodes << itemPriv->itemNodeInstance
3921 << itemPriv->opacityNode()
3922 << itemPriv->clipNode()
3923 << itemPriv->rootNode()
3924 << itemPriv->paintNode;
3925 nodes.removeAll(t: 0);
3926
3927 Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance);
3928 for (int i=1; i<nodes.size(); ++i) {
3929 QSGNode *n = nodes.at(i);
3930 // Failing this means we messed up reparenting
3931 Q_ASSERT(n->parent() == nodes.at(i-1));
3932 // Only the paintNode and the one who is childContainer may have more than one child.
3933 Q_ASSERT(n == itemPriv->paintNode || n == itemPriv->childContainerNode() || n->childCount() == 1);
3934 }
3935#endif
3936
3937}
3938
3939bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg)
3940{
3941 Q_Q(QQuickWindow);
3942 static const QMetaMethod errorSignal = QMetaMethod::fromSignal(signal: &QQuickWindow::sceneGraphError);
3943 if (q->isSignalConnected(signal: errorSignal)) {
3944 emit q->sceneGraphError(error, message: msg);
3945 return true;
3946 }
3947 return false;
3948}
3949
3950void QQuickWindow::maybeUpdate()
3951{
3952 Q_D(QQuickWindow);
3953 if (d->renderControl)
3954 QQuickRenderControlPrivate::get(renderControl: d->renderControl)->maybeUpdate();
3955 else if (d->windowManager)
3956 d->windowManager->maybeUpdate(window: this);
3957}
3958
3959void QQuickWindow::cleanupSceneGraph()
3960{
3961 Q_D(QQuickWindow);
3962#if QT_CONFIG(opengl)
3963 delete d->vaoHelper;
3964 d->vaoHelper = nullptr;
3965#endif
3966 if (!d->renderer)
3967 return;
3968
3969 delete d->renderer->rootNode();
3970 delete d->renderer;
3971 d->renderer = nullptr;
3972
3973 d->runAndClearJobs(jobs: &d->beforeSynchronizingJobs);
3974 d->runAndClearJobs(jobs: &d->afterSynchronizingJobs);
3975 d->runAndClearJobs(jobs: &d->beforeRenderingJobs);
3976 d->runAndClearJobs(jobs: &d->afterRenderingJobs);
3977 d->runAndClearJobs(jobs: &d->afterSwapJobs);
3978}
3979
3980void QQuickWindow::setTransientParent_helper(QQuickWindow *window)
3981{
3982 qCDebug(lcTransient) << this << "is transient for" << window;
3983 setTransientParent(window);
3984 disconnect(sender: sender(), SIGNAL(windowChanged(QQuickWindow*)),
3985 receiver: this, SLOT(setTransientParent_helper(QQuickWindow*)));
3986}
3987
3988/*!
3989 Returns the OpenGL context used for rendering.
3990
3991 \note If the scene graph is not ready, or the scene graph is not using
3992 OpenGL (or RHI over OpenGL), this function will return null.
3993
3994 \sa sceneGraphInitialized(), sceneGraphInvalidated()
3995 */
3996QOpenGLContext *QQuickWindow::openglContext() const
3997{
3998#if QT_CONFIG(opengl)
3999 Q_D(const QQuickWindow);
4000 if (d->context && d->context->isValid()) {
4001 QSGRendererInterface *rif = d->context->sceneGraphContext()->rendererInterface(renderContext: d->context);
4002 if (rif) {
4003 return reinterpret_cast<QOpenGLContext *>(rif->getResource(window: const_cast<QQuickWindow *>(this),
4004 resource: QSGRendererInterface::OpenGLContextResource));
4005 }
4006 }
4007#endif
4008 return nullptr;
4009}
4010
4011/*!
4012 Returns true if the scene graph has been initialized; otherwise returns false.
4013 */
4014bool QQuickWindow::isSceneGraphInitialized() const
4015{
4016 Q_D(const QQuickWindow);
4017 return d->context != nullptr && d->context->isValid();
4018}
4019
4020/*!
4021 \fn void QQuickWindow::frameSwapped()
4022
4023 This signal is emitted when a frame has been queued for presenting. With
4024 vertical synchronization enabled the signal is emitted at most once per
4025 vsync interval in a continuously animating scene.
4026
4027 This signal will be emitted from the scene graph rendering thread.
4028*/
4029
4030/*!
4031 \qmlsignal QtQuick.Window::Window::frameSwapped()
4032
4033 This signal is emitted when a frame has been queued for presenting. With
4034 vertical synchronization enabled the signal is emitted at most once per
4035 vsync interval in a continuously animating scene.
4036 */
4037
4038/*!
4039 \fn void QQuickWindow::sceneGraphInitialized()
4040
4041 This signal is emitted when the scene graph has been initialized.
4042
4043 This signal will be emitted from the scene graph rendering thread.
4044 */
4045
4046/*!
4047 \qmlsignal QtQuick.Window::Window::sceneGraphInitialized()
4048 \internal
4049 */
4050
4051/*!
4052 \fn void QQuickWindow::sceneGraphInvalidated()
4053
4054 This signal is emitted when the scene graph has been invalidated.
4055
4056 This signal implies that the graphics rendering context used
4057 has been invalidated and all user resources tied to that context
4058 should be released.
4059
4060 In the case of the default OpenGL adaptation the context of this
4061 window will be bound when this function is called. The only exception
4062 is if the native OpenGL has been destroyed outside Qt's control,
4063 for instance through EGL_CONTEXT_LOST.
4064
4065 This signal will be emitted from the scene graph rendering thread.
4066 */
4067
4068/*!
4069 \qmlsignal QtQuick.Window::Window::sceneGraphInvalidated()
4070 \internal
4071 */
4072
4073/*!
4074 \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message)
4075
4076 This signal is emitted when an \a error occurred during scene graph initialization.
4077
4078 Applications should connect to this signal if they wish to handle errors,
4079 like graphics context creation failures, in a custom way. When no slot is
4080 connected to the signal, the behavior will be different: Quick will print
4081 the \a message, or show a message box, and terminate the application.
4082
4083 This signal will be emitted from the GUI thread.
4084
4085 \since 5.3
4086 */
4087
4088/*!
4089 \qmlsignal QtQuick.Window::Window::sceneGraphError(SceneGraphError error, QString message)
4090
4091 This signal is emitted when an \a error occurred during scene graph initialization.
4092
4093 You can implement onSceneGraphError(error, message) to handle errors,
4094 such as graphics context creation failures, in a custom way.
4095 If no handler is connected to this signal, Quick will print the \a message,
4096 or show a message box, and terminate the application.
4097
4098 \since 5.3
4099 */
4100
4101/*!
4102 \class QQuickCloseEvent
4103 \internal
4104 \since 5.1
4105
4106 \inmodule QtQuick
4107
4108 \brief Notification that a \l QQuickWindow is about to be closed
4109*/
4110/*!
4111 \qmltype CloseEvent
4112 \instantiates QQuickCloseEvent
4113 \inqmlmodule QtQuick.Window
4114 \ingroup qtquick-visual
4115 \brief Notification that a \l Window is about to be closed.
4116 \since 5.1
4117
4118 Notification that a window is about to be closed by the windowing system
4119 (e.g. the user clicked the title bar close button). The CloseEvent contains
4120 an accepted property which can be set to false to abort closing the window.
4121
4122 \sa QQuickWindow::closing()
4123*/
4124
4125/*!
4126 \qmlproperty bool CloseEvent::accepted
4127
4128 This property indicates whether the application will allow the user to
4129 close the window. It is true by default.
4130*/
4131
4132/*!
4133 \fn void QQuickWindow::closing(QQuickCloseEvent *close)
4134 \since 5.1
4135
4136 This signal is emitted when the window receives the event \a close from
4137 the windowing system.
4138
4139 On \macOs, Qt will create a menu item \c Quit if there is no menu item
4140 whose text is "quit" or "exit". This menu item calls the \c QCoreApplication::quit
4141 signal, not the \c QQuickWindow::closing() signal.
4142
4143 \sa {QMenuBar as a Global Menu Bar}
4144*/
4145
4146/*!
4147 \qmlsignal QtQuick.Window::Window::closing(CloseEvent close)
4148 \since 5.1
4149
4150 This signal is emitted when the user tries to close the window.
4151
4152 This signal includes a \a close parameter. The \c {close.accepted}
4153 property is true by default so that the window is allowed to close; but you
4154 can implement an \c onClosing handler and set \c {close.accepted = false} if
4155 you need to do something else before the window can be closed.
4156 */
4157
4158#if QT_CONFIG(opengl)
4159/*!
4160 Sets the render target for this window to be \a fbo.
4161
4162 The specified fbo must be created in the context of the window
4163 or one that shares with it.
4164
4165 \note
4166 This function only has an effect when using the default OpenGL scene
4167 graph adaptation.
4168
4169 \note This function has no effect when running on the RHI graphics abstraction.
4170
4171 \warning
4172 This function can only be called from the thread doing
4173 the rendering.
4174 */
4175
4176void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
4177{
4178 Q_D(QQuickWindow);
4179 if (d->rhi)
4180 return;
4181
4182 if (d->context && QThread::currentThread() != d->context->thread()) {
4183 qWarning(msg: "QQuickWindow::setRenderTarget: Cannot set render target from outside the rendering thread");
4184 return;
4185 }
4186
4187 d->renderTarget = fbo;
4188 if (fbo) {
4189 d->renderTargetId = fbo->handle();
4190 d->renderTargetSize = fbo->size();
4191 } else {
4192 d->renderTargetId = 0;
4193 d->renderTargetSize = QSize();
4194 }
4195}
4196#endif
4197/*!
4198 \overload
4199
4200 Sets the render target for this window to be an FBO with
4201 \a fboId and \a size.
4202
4203 The specified FBO must be created in the context of the window
4204 or one that shares with it.
4205
4206 \note \a fboId can also be set to 0. In this case rendering will target the
4207 default framebuffer of whichever surface is current when the scenegraph
4208 renders. \a size must still be valid, specifying the dimensions of the
4209 surface.
4210
4211 \note
4212 This function only has an effect when using the default OpenGL scene
4213 graph adaptation.
4214
4215 \warning
4216 This function can only be called from the thread doing
4217 the rendering.
4218*/
4219
4220void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
4221{
4222 Q_D(QQuickWindow);
4223 if (d->context && QThread::currentThread() != d->context->thread()) {
4224 qWarning(msg: "QQuickWindow::setRenderThread: Cannot set render target from outside the rendering thread");
4225 return;
4226 }
4227
4228 d->renderTargetId = fboId;
4229 d->renderTargetSize = size;
4230
4231 // Unset any previously set instance...
4232 d->renderTarget = nullptr;
4233}
4234
4235
4236/*!
4237 Returns the FBO id of the render target when set; otherwise returns 0.
4238 */
4239uint QQuickWindow::renderTargetId() const
4240{
4241 Q_D(const QQuickWindow);
4242 return d->renderTargetId;
4243}
4244
4245/*!
4246 Returns the size of the currently set render target; otherwise returns an empty size.
4247 */
4248QSize QQuickWindow::renderTargetSize() const
4249{
4250 Q_D(const QQuickWindow);
4251 return d->renderTargetSize;
4252}
4253
4254
4255
4256#if QT_CONFIG(opengl)
4257/*!
4258 Returns the render target for this window.
4259
4260 The default is to render to the surface of the window, in which
4261 case the render target is 0.
4262
4263 \note This function will return nullptr when not using the OpenGL scene
4264 graph adaptation.
4265
4266 \note This function has no effect and returns nullptr when running on the
4267 RHI graphics abstraction.
4268 */
4269QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
4270{
4271 Q_D(const QQuickWindow);
4272 return d->renderTarget;
4273}
4274#endif
4275
4276/*!
4277 Grabs the contents of the window and returns it as an image.
4278
4279 It is possible to call the grabWindow() function when the window is not
4280 visible. This requires that the window is \l{QWindow::create()} {created}
4281 and has a valid size and that no other QQuickWindow instances are rendering
4282 in the same process.
4283
4284 \warning Calling this function will cause performance problems.
4285
4286 \warning This function can only be called from the GUI thread.
4287 */
4288QImage QQuickWindow::grabWindow()
4289{
4290 Q_D(QQuickWindow);
4291
4292 if (!isVisible() && !d->renderControl) {
4293 // backends like software and d3d12 can grab regardless of the window state
4294 if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose))
4295 return d->windowManager->grab(window: this);
4296 }
4297
4298 if (!isVisible() && !d->renderControl) {
4299 if (d->rhi) {
4300 // ### we may need a full offscreen round when non-exposed...
4301
4302 if (d->renderControl)
4303 return d->renderControl->grab();
4304 else if (d->windowManager)
4305 return d->windowManager->grab(window: this);
4306 return QImage();
4307 }
4308
4309#if QT_CONFIG(opengl)
4310 auto openglRenderContext = static_cast<QSGDefaultRenderContext *>(d->context);
4311 if (!openglRenderContext->openglContext()) {
4312 if (!handle() || !size().isValid()) {
4313 qWarning(msg: "QQuickWindow::grabWindow: window must be created and have a valid size");
4314 return QImage();
4315 }
4316
4317 QOpenGLContext context;
4318 context.setFormat(requestedFormat());
4319 context.setShareContext(qt_gl_global_share_context());
4320 context.create();
4321 context.makeCurrent(surface: this);
4322 QSGDefaultRenderContext::InitParams rcParams;
4323 rcParams.openGLContext = &context;
4324 d->context->initialize(params: &rcParams);
4325
4326 d->polishItems();
4327 d->syncSceneGraph();
4328 d->renderSceneGraph(size: size());
4329
4330 bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255;
4331 QImage image = qt_gl_read_framebuffer(size: size() * effectiveDevicePixelRatio(), alpha_format: alpha, include_alpha: alpha);
4332 image.setDevicePixelRatio(effectiveDevicePixelRatio());
4333 d->cleanupNodesOnShutdown();
4334 d->context->invalidate();
4335 context.doneCurrent();
4336
4337 return image;
4338 }
4339#endif
4340 }
4341
4342 if (d->renderControl)
4343 return d->renderControl->grab();
4344 else if (d->windowManager)
4345 return d->windowManager->grab(window: this);
4346 return QImage();
4347}
4348
4349/*!
4350 Returns an incubation controller that splices incubation between frames
4351 for this window. QQuickView automatically installs this controller for you,
4352 otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}.
4353
4354 The controller is owned by the window and will be destroyed when the window
4355 is deleted.
4356*/
4357QQmlIncubationController *QQuickWindow::incubationController() const
4358{
4359 Q_D(const QQuickWindow);
4360
4361 if (!d->windowManager)
4362 return nullptr; // TODO: make sure that this is safe
4363
4364 if (!d->incubationController)
4365 d->incubationController = new QQuickWindowIncubationController(d->windowManager);
4366 return d->incubationController;
4367}
4368
4369
4370
4371/*!
4372 \enum QQuickWindow::CreateTextureOption
4373
4374 The CreateTextureOption enums are used to customize a texture is wrapped.
4375
4376 \value TextureHasAlphaChannel The texture has an alpha channel and should
4377 be drawn using blending.
4378
4379 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
4380 mipmapping enabled.
4381
4382 \value TextureOwnsGLTexture The texture object owns the texture id and
4383 will delete the OpenGL texture when the texture object is deleted.
4384
4385 \value TextureCanUseAtlas The image can be uploaded into a texture atlas.
4386
4387 \value TextureIsOpaque The texture will return false for
4388 QSGTexture::hasAlphaChannel() and will not be blended. This flag was added
4389 in Qt 5.6.
4390
4391 */
4392
4393/*!
4394 \enum QQuickWindow::SceneGraphError
4395
4396 This enum describes the error in a sceneGraphError() signal.
4397
4398 \value ContextNotAvailable graphics context creation failed. This typically means that
4399 no suitable OpenGL implementation was found, for example because no graphics drivers
4400 are installed and so no OpenGL 2 support is present. On mobile and embedded boards
4401 that use OpenGL ES such an error is likely to indicate issues in the windowing system
4402 integration and possibly an incorrect configuration of Qt.
4403
4404 \since 5.3
4405 */
4406
4407/*!
4408 \enum QQuickWindow::TextRenderType
4409 \since 5.10
4410
4411 This enum describes the default render type of text-like elements in Qt
4412 Quick (\l Text, \l TextInput, etc.).
4413
4414 Select NativeTextRendering if you prefer text to look native on the target
4415 platform and do not require advanced features such as transformation of the
4416 text. Using such features in combination with the NativeTextRendering
4417 render type will lend poor and sometimes pixelated results.
4418
4419 \value QtTextRendering Use Qt's own rasterization algorithm.
4420
4421 \value NativeTextRendering Use the operating system's native rasterizer for text.
4422*/
4423
4424/*!
4425 \fn void QQuickWindow::beforeSynchronizing()
4426
4427 This signal is emitted before the scene graph is synchronized with the QML state.
4428
4429 This signal can be used to do any preparation required before calls to
4430 QQuickItem::updatePaintNode().
4431
4432 The OpenGL context used for rendering the scene graph will be bound at this point.
4433
4434 \warning This signal is emitted from the scene graph rendering thread. If your
4435 slot function needs to finish before execution continues, you must make sure that
4436 the connection is direct (see Qt::ConnectionType).
4437
4438 \warning Make very sure that a signal handler for beforeSynchronizing leaves the GL
4439 context in the same state as it was when the signal handler was entered. Failing to
4440 do so can result in the scene not rendering properly.
4441
4442 \sa resetOpenGLState()
4443*/
4444
4445/*!
4446 \qmlsignal QtQuick.Window::Window::beforeSynchronizing()
4447 \internal
4448*/
4449
4450/*!
4451 \fn void QQuickWindow::afterSynchronizing()
4452
4453 This signal is emitted after the scene graph is synchronized with the QML state.
4454
4455 This signal can be used to do preparation required after calls to
4456 QQuickItem::updatePaintNode(), while the GUI thread is still locked.
4457
4458 The graphics context used for rendering the scene graph will be bound at this point.
4459
4460 \warning This signal is emitted from the scene graph rendering thread. If your
4461 slot function needs to finish before execution continues, you must make sure that
4462 the connection is direct (see Qt::ConnectionType).
4463
4464 \warning When using the OpenGL adaptation, make sure that a signal handler for
4465 afterSynchronizing leaves the OpenGL context in the same state as it was when the
4466 signal handler was entered. Failing to do so can result in the scene not rendering
4467 properly.
4468
4469 \since 5.3
4470 \sa resetOpenGLState()
4471 */
4472
4473/*!
4474 \qmlsignal QtQuick.Window::Window::afterSynchronizing()
4475 \internal
4476 \since 5.3
4477 */
4478
4479/*!
4480 \fn void QQuickWindow::beforeRendering()
4481
4482 This signal is emitted before the scene starts rendering.
4483
4484 Combined with the modes for clearing the background, this option
4485 can be used to paint using raw OpenGL under QML content.
4486
4487 The OpenGL context used for rendering the scene graph will be bound
4488 at this point.
4489
4490 When using the RHI, the signal is emitted after the preparations for the
4491 frame have been done, meaning there is a command buffer in recording mode,
4492 where applicable. If desired, the slot function connected to this signal
4493 can query native resources like the command before via
4494 QSGRendererInterface. Note however that the recording of the main render
4495 pass is not yet started at this point and it is not possible to add
4496 commands within that pass. Starting a pass means clearing the color, depth,
4497 and stencil buffers so it is not possible to achieve an underlay type of
4498 rendering by just connecting to this signal. Rather, connect to
4499 beforeRenderPassRecording(). However, connecting to this signal is still
4500 important if the recording of copy type of commands is desired since those
4501 cannot be enqueued within a render pass.
4502
4503 \warning This signal is emitted from the scene graph rendering thread. If your
4504 slot function needs to finish before execution continues, you must make sure that
4505 the connection is direct (see Qt::ConnectionType).
4506
4507 \warning Make very sure that a signal handler for beforeRendering leaves the OpenGL
4508 context in the same state as it was when the signal handler was entered. Failing to
4509 do so can result in the scene not rendering properly.
4510
4511 \sa resetOpenGLState()
4512*/
4513
4514/*!
4515 \qmlsignal QtQuick.Window::Window::beforeRendering()
4516 \internal
4517*/
4518
4519/*!
4520 \fn void QQuickWindow::afterRendering()
4521
4522 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
4523
4524 This signal can be used to paint using raw OpenGL on top of QML content,
4525 or to do screen scraping of the current frame buffer.
4526
4527 The OpenGL context used for rendering the scene graph will be bound at this point.
4528
4529 When using the RHI, the signal is emitted after scene graph has added its
4530 commands to the command buffer, which is not yet submitted to the graphics
4531 queue. If desired, the slot function connected to this signal can query
4532 native resources, like the command buffer, before via QSGRendererInterface.
4533 Note however that the render pass (or passes) are already recorded at this
4534 point and it is not possible to add more commands within the scenegraph's
4535 pass. Instead, use afterRenderPassRecording() for that. This signal has
4536 therefore limited use and is rarely needed in an RHI-based setup. Rather,
4537 it is the combination of beforeRendering() + beforeRenderPassRecording() or
4538 beforeRendering() + afterRenderPassRecording() that is typically used to
4539 achieve under- or overlaying of the custom rendering.
4540
4541 \warning This signal is emitted from the scene graph rendering thread. If your
4542 slot function needs to finish before execution continues, you must make sure that
4543 the connection is direct (see Qt::ConnectionType).
4544
4545 \warning Make very sure that a signal handler for afterRendering() leaves the OpenGL
4546 context in the same state as it was when the signal handler was entered. Failing to
4547 do so can result in the scene not rendering properly.
4548
4549 \sa resetOpenGLState()
4550 */
4551
4552/*!
4553 \qmlsignal QtQuick.Window::Window::afterRendering()
4554 \internal
4555 */
4556
4557/*!
4558 \fn void QQuickWindow::beforeRenderPassRecording()
4559
4560 This signal is emitted before the scenegraph starts recording commands for
4561 the main render pass. (Layers have their own passes and are fully recorded
4562 by the time this signal is emitted.) The render pass is already active on
4563 the command buffer when the signal is emitted.
4564
4565 This signal is applicable when using the RHI graphics abstraction with the
4566 scenegraph. It is emitted later than beforeRendering() and it guarantees
4567 that not just the frame, but also the recording of the scenegraph's main
4568 render pass is active. This allows inserting commands without having to
4569 generate an entire, separate render pass (which would typically clear the
4570 attached images). The native graphics objects can be queried via
4571 QSGRendererInterface.
4572
4573 When not running with the RHI (and using OpenGL directly), the signal is
4574 emitted after the renderer has cleared the render target. This makes it
4575 possible to create applications that function identically both with and
4576 without the RHI.
4577
4578 \note Resource updates (uploads, copies) typically cannot be enqueued from
4579 within a render pass. Therefore, more complex user rendering will need to
4580 connect to both beforeRendering() and this signal.
4581
4582 \warning This signal is emitted from the scene graph rendering thread. If your
4583 slot function needs to finish before execution continues, you must make sure that
4584 the connection is direct (see Qt::ConnectionType).
4585
4586 \since 5.14
4587*/
4588
4589/*!
4590 \qmlsignal QtQuick.Window::Window::beforeRenderPassRecording()
4591 \internal
4592 \since 5.14
4593*/
4594
4595/*!
4596 \fn void QQuickWindow::afterRenderPassRecording()
4597
4598 This signal is emitted after the scenegraph has recorded the commands for
4599 its main render pass, but the pass is not yet finalized on the command
4600 buffer.
4601
4602 This signal is applicable when using the RHI graphics abstraction with the
4603 scenegraph. It is emitted earlier than afterRendering() and it guarantees
4604 that not just the frame, but also the recording of the scenegraph's main
4605 render pass is still active. This allows inserting commands without having
4606 to generate an entire, separate render pass (which would typically clear
4607 the attached images). The native graphics objects can be queried via
4608 QSGRendererInterface.
4609
4610 When not running with the RHI (and using OpenGL directly), the signal is
4611 emitted after the renderer has finished its rendering, but before
4612 afterRendering(). This makes it possible to create applications that
4613 function identically both with and without the RHI.
4614
4615 \note Resource updates (uploads, copies) typically cannot be enqueued from
4616 within a render pass. Therefore, more complex user rendering will need to
4617 connect to both beforeRendering() and this signal.
4618
4619 \warning This signal is emitted from the scene graph rendering thread. If your
4620 slot function needs to finish before execution continues, you must make sure that
4621 the connection is direct (see Qt::ConnectionType).
4622
4623 \since 5.14
4624*/
4625
4626/*!
4627 \qmlsignal QtQuick.Window::Window::afterRenderPassRecording()
4628 \internal
4629 \since 5.14
4630*/
4631
4632/*!
4633 \fn void QQuickWindow::afterAnimating()
4634
4635 This signal is emitted on the GUI thread before requesting the render thread to
4636 perform the synchronization of the scene graph.
4637
4638 Unlike the other similar signals, this one is emitted on the GUI thread
4639 instead of the render thread. It can be used to synchronize external
4640 animation systems with the QML content. At the same time this means that
4641 this signal is not suitable for triggering graphics operations.
4642
4643 \since 5.3
4644 */
4645
4646/*!
4647 \qmlsignal QtQuick.Window::Window::afterAnimating()
4648
4649 This signal is emitted on the GUI thread before requesting the render thread to
4650 perform the synchronization of the scene graph.
4651
4652 You can implement onAfterAnimating to do additional processing after each animation step.
4653
4654 \since 5.3
4655 */
4656
4657/*!
4658 \fn void QQuickWindow::openglContextCreated(QOpenGLContext *context)
4659
4660 This signal is emitted on the GUI thread when the OpenGL \a context
4661 for this window is created, before it is made current.
4662
4663 Some implementations will share the same OpenGL context between
4664 multiple QQuickWindow instances. The openglContextCreated() signal
4665 will in this case only be emitted for the first window, when the
4666 OpenGL context is actually created.
4667
4668 QQuickWindow::openglContext() will still return 0 for this window
4669 until after the QQuickWindow::sceneGraphInitialized() has been
4670 emitted.
4671
4672 \note
4673 This signal will only be emmited when using the default OpenGL scene
4674 graph adaptation.
4675
4676 \since 5.3
4677 */
4678
4679/*!
4680 \qmlsignal QtQuick.Window::Window::openglContextCreated()
4681 \internal
4682 \since 5.3
4683 */
4684
4685/*!
4686 \fn void QQuickWindow::sceneGraphAboutToStop()
4687
4688 This signal is emitted on the render thread when the scene graph is
4689 about to stop rendering. This happens usually because the window
4690 has been hidden.
4691
4692 Applications may use this signal to release resources, but should be
4693 prepared to reinstantiated them again fast. The scene graph and the
4694 graphics context are not released at this time.
4695
4696 \warning This signal is emitted from the scene graph rendering thread. If your
4697 slot function needs to finish before execution continues, you must make sure that
4698 the connection is direct (see Qt::ConnectionType).
4699
4700 \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the
4701 graphics context in the same state as it was when the signal handler was entered.
4702 Failing to do so can result in the scene not rendering properly.
4703
4704 \sa sceneGraphInvalidated(), resetOpenGLState()
4705 \since 5.3
4706 */
4707
4708/*!
4709 \qmlsignal QtQuick.Window::Window::sceneGraphAboutToStop()
4710 \internal
4711 \since 5.3
4712 */
4713
4714/*!
4715 Sets whether the scene graph rendering of QML should clear the color buffer
4716 before it starts rendering to \a enabled.
4717
4718 By disabling clearing of the color buffer, it is possible to render OpengGL content
4719 under the scene graph.
4720
4721 The color buffer is cleared by default.
4722
4723 \warning This flag is ignored completely when running with the RHI graphics
4724 abstraction instead of using OpenGL directly. As explicit clear commands
4725 simply do not exist in some modern APIs, the scene graph cannot offer this
4726 flexibility anymore. The images associated with a render target will always
4727 get cleared when a render pass starts. As a solution, an alternative to
4728 disabling scene graph issued clears is provided in form of the
4729 beforeRenderPassRecording() signal.
4730
4731 \sa beforeRendering(), beforeRenderPassRecording()
4732 */
4733
4734void QQuickWindow::setClearBeforeRendering(bool enabled)
4735{
4736 Q_D(QQuickWindow);
4737 d->clearBeforeRendering = enabled;
4738}
4739
4740
4741
4742/*!
4743 Returns whether clearing of the color buffer is done before rendering or not.
4744 */
4745
4746bool QQuickWindow::clearBeforeRendering() const
4747{
4748 Q_D(const QQuickWindow);
4749 return d->clearBeforeRendering;
4750}
4751
4752/*!
4753 \overload
4754 */
4755
4756QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const
4757{
4758 return createTextureFromImage(image, options: {});
4759}
4760
4761
4762/*!
4763 Creates a new QSGTexture from the supplied \a image. If the image has an
4764 alpha channel, the corresponding texture will have an alpha channel.
4765
4766 The caller of the function is responsible for deleting the returned texture.
4767 For example whe using the OpenGL adaptation the actual OpenGL texture will
4768 be deleted when the texture object is deleted.
4769
4770 When \a options contains TextureCanUseAtlas, the engine may put the image
4771 into a texture atlas. Textures in an atlas need to rely on
4772 QSGTexture::normalizedTextureSubRect() for their geometry and will not
4773 support QSGTexture::Repeat. Other values from CreateTextureOption are
4774 ignored.
4775
4776 When \a options contains TextureIsOpaque, the engine will create an RGB
4777 texture which returns false for QSGTexture::hasAlphaChannel(). Opaque
4778 textures will in most cases be faster to render. When this flag is not set,
4779 the texture will have an alpha channel based on the image's format.
4780
4781 When \a options contains TextureHasMipmaps, the engine will create a
4782 texture which can use mipmap filtering. Mipmapped textures can not be in
4783 an atlas.
4784
4785 When using the OpenGL adaptation, the returned texture will be using
4786 \c GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format.
4787 Reimplement QSGTexture to create textures with different parameters.
4788
4789 \warning This function will return 0 if the scene graph has not yet been
4790 initialized.
4791
4792 \warning The returned texture is not memory managed by the scene graph and
4793 must be explicitly deleted by the caller on the rendering thread.
4794 This is achieved by deleting the texture from a QSGNode destructor
4795 or by using deleteLater() in the case where the texture already has affinity
4796 to the rendering thread.
4797
4798 This function can be called from any thread.
4799
4800 \sa sceneGraphInitialized(), QSGTexture
4801 */
4802
4803QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const
4804{
4805 Q_D(const QQuickWindow);
4806 if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid()
4807 return nullptr;
4808 uint flags = 0;
4809 if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas;
4810 if (options & TextureHasMipmaps) flags |= QSGRenderContext::CreateTexture_Mipmap;
4811 if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha;
4812 return d->context->createTexture(image, flags);
4813}
4814
4815
4816#if QT_DEPRECATED_SINCE(5, 15)
4817/*!
4818 Creates a new QSGTexture object from an existing OpenGL texture \a id and \a size.
4819
4820 The caller of the function is responsible for deleting the returned texture.
4821
4822 The returned texture will be using \c GL_TEXTURE_2D as texture target and
4823 assumes that internal format is \c {GL_RGBA}. Reimplement QSGTexture to
4824 create textures with different parameters.
4825
4826 Use \a options to customize the texture attributes. The TextureUsesAtlas
4827 option is ignored.
4828
4829 \warning This function will return null if the scenegraph has not yet been
4830 initialized or OpenGL is not in use.
4831
4832 \note This function only has an effect when using the default OpenGL scene graph
4833 adaptation.
4834
4835 \note This function has no effect when running on the RHI graphics
4836 abstraction. Use createTextureFromNativeObject() instead.
4837
4838 \obsolete
4839
4840 \sa sceneGraphInitialized(), QSGTexture
4841 */
4842QSGTexture *QQuickWindow::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
4843{
4844#if QT_CONFIG(opengl)
4845 Q_D(const QQuickWindow);
4846 if (!d->rhi) {
4847 if (openglContext()) {
4848 QSGPlainTexture *texture = new QSGPlainTexture();
4849 texture->setTextureId(id);
4850 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
4851 texture->setOwnsTexture(options & TextureOwnsGLTexture);
4852 texture->setTextureSize(size);
4853 return texture;
4854 }
4855 } else {
4856 qWarning(msg: "createTextureFromId() must not be called when running on the RHI. "
4857 "Use createTextureFromNativeObject() instead.");
4858 }
4859#else
4860 Q_UNUSED(id)
4861 Q_UNUSED(size)
4862 Q_UNUSED(options)
4863#endif
4864 return nullptr;
4865}
4866#endif
4867
4868/*!
4869 \enum QQuickWindow::NativeObjectType
4870 \since 5.14
4871
4872 Specifies the type of the native object passed to functions such as
4873 createTextureFromNativeObject().
4874
4875 \value NativeObjectTexture The native object is a 2D texture (OpenGL,
4876 Direct3D 11, Metal) or image (Vulkan).
4877 */
4878
4879/*!
4880 Creates a new QSGTexture object from an existing native object.
4881
4882 The native object is wrapped, but not owned, by the resulting QSGTexture.
4883 The caller of the function is responsible for deleting the returned
4884 QSGTexture, but that will not destroy the underlying native object.
4885
4886 \a type specifies the type of the object. In practice the type is
4887 NativeObjectTexture, indicating that the native object is a texture or
4888 image of the underlying graphics API. Other types may be introduced in the
4889 future.
4890
4891 This function is currently suitable for 2D RGBA textures only.
4892
4893 Unlike createTextureFromId(), this function supports both direct OpenGL
4894 usage and the RHI abstracted rendering path.
4895
4896 \warning This function will return null if the scenegraph has not yet been
4897 initialized.
4898
4899 Use \a options to customize the texture attributes. Only the
4900 TextureHasAlphaChannel and TextureHasMipmaps are taken into account here.
4901
4902 \warning Unlike createTextureFromId(), this function never takes ownership
4903 of the native object, and the TextureOwnsGLTexture flag is ignored.
4904
4905 \a size specifies the size in pixels.
4906
4907 \a nativeObjectPtr is a pointer to the native object handle. With OpenGL,
4908 the native handle is a GLuint value, so \a nativeObjectPtr is then a
4909 pointer to a GLuint. With Vulkan, the native handle is a VkImage, so \a
4910 nativeObjectPtr is a pointer to a VkImage. With Direct3D 11 and Metal \a
4911 nativeObjectPtr is a pointer to a ID3D11Texture2D or MTLTexture pointer.
4912
4913 \note Pay attention to the fact that \a nativeObjectPtr is always a pointer
4914 to the native texture handle type, even if the native type itself is a
4915 pointer.
4916
4917 \a nativeLayout is only used for APIs like Vulkan. When applicable, it must
4918 specify the current image layout, such as, a VkImageLayout value.
4919
4920 \sa sceneGraphInitialized(), QSGTexture, QSGTexture::nativeTexture()
4921
4922 \since 5.14
4923 */
4924QSGTexture *QQuickWindow::createTextureFromNativeObject(NativeObjectType type,
4925 const void *nativeObjectPtr,
4926 int nativeLayout,
4927 const QSize &size,
4928 CreateTextureOptions options) const
4929{
4930 if (type != NativeObjectTexture) {
4931 qWarning(msg: "createTextureFromNativeObject: only textures are supported");
4932 return nullptr;
4933 }
4934
4935#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
4936 Q_D(const QQuickWindow);
4937 if (d->rhi) {
4938 QSGPlainTexture *texture = new QSGPlainTexture;
4939 texture->setTextureFromNativeObject(rhi: d->rhi, type, nativeObjectPtr, nativeLayout,
4940 size, mipmap: options.testFlag(flag: TextureHasMipmaps));
4941 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
4942 // note that the QRhiTexture does not (and cannot) own the native object
4943 texture->setOwnsTexture(true); // texture meaning the QRhiTexture here, not the native object
4944 texture->setTextureSize(size);
4945 return texture;
4946 } else if (openglContext()) {
4947 QSGPlainTexture *texture = new QSGPlainTexture;
4948 texture->setTextureId(*reinterpret_cast<const uint *>(nativeObjectPtr));
4949 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
4950 texture->setOwnsTexture(options & TextureOwnsGLTexture);
4951 texture->setTextureSize(size);
4952 return texture;
4953 }
4954#else
4955 Q_UNUSED(nativeObjectPtr);
4956 Q_UNUSED(nativeLayout);
4957 Q_UNUSED(size);
4958 Q_UNUSED(options);
4959#endif
4960
4961 return nullptr;
4962}
4963
4964/*!
4965 \qmlproperty color Window::color
4966
4967 The background color for the window.
4968
4969 Setting this property is more efficient than using a separate Rectangle.
4970*/
4971
4972/*!
4973 \property QQuickWindow::color
4974 \brief The color used to clear the OpenGL context.
4975
4976 Setting the clear color has no effect when clearing is disabled.
4977 By default, the clear color is white.
4978
4979 \sa setClearBeforeRendering(), setDefaultAlphaBuffer()
4980 */
4981
4982void QQuickWindow::setColor(const QColor &color)
4983{
4984 Q_D(QQuickWindow);
4985 if (color == d->clearColor)
4986 return;
4987
4988 if (color.alpha() != d->clearColor.alpha()) {
4989 QSurfaceFormat fmt = requestedFormat();
4990 if (color.alpha() < 255)
4991 fmt.setAlphaBufferSize(8);
4992 else
4993 fmt.setAlphaBufferSize(-1);
4994 setFormat(fmt);
4995 }
4996 d->clearColor = color;
4997 emit colorChanged(color);
4998 update();
4999}
5000
5001QColor QQuickWindow::color() const
5002{
5003 return d_func()->clearColor;
5004}
5005
5006/*!
5007 \brief Returns whether to use alpha transparency on newly created windows.
5008
5009 \since 5.1
5010 \sa setDefaultAlphaBuffer()
5011 */
5012bool QQuickWindow::hasDefaultAlphaBuffer()
5013{
5014 return QQuickWindowPrivate::defaultAlphaBuffer;
5015}
5016
5017/*!
5018 \brief \a useAlpha specifies whether to use alpha transparency on newly created windows.
5019 \since 5.1
5020
5021 In any application which expects to create translucent windows, it's necessary to set
5022 this to true before creating the first QQuickWindow. The default value is false.
5023
5024 \sa hasDefaultAlphaBuffer()
5025 */
5026void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
5027{
5028 QQuickWindowPrivate::defaultAlphaBuffer = useAlpha;
5029}
5030#if QT_CONFIG(opengl)
5031/*!
5032 \since 5.2
5033
5034 Call this function to reset the OpenGL context its default state.
5035
5036 The scene graph uses the OpenGL context and will both rely on and
5037 clobber its state. When mixing raw OpenGL commands with scene
5038 graph rendering, this function provides a convenient way of
5039 resetting the OpenGL context state back to its default values.
5040
5041 This function does not touch state in the fixed-function pipeline.
5042
5043 This function does not clear the color, depth and stencil buffers. Use
5044 QQuickWindow::setClearBeforeRendering to control clearing of the color
5045 buffer. The depth and stencil buffer might be clobbered by the scene
5046 graph renderer. Clear these manually on demand.
5047
5048 \note This function only has an effect when using the default OpenGL scene graph
5049 adaptation.
5050
5051 \note This function will only reset the OpenGL context in relation to what has been changed
5052 internally as part of the OpenGL scene graph. It does not reset anything that has been changed
5053 externally such as direct OpenGL calls done inside the application code if those same calls are
5054 not used internally.
5055
5056 \note This function has no effect when running on the RHI graphics
5057 abstraction and the underlying RHI backend is not OpenGL.
5058
5059 \sa QQuickWindow::beforeRendering(), beginExternalCommands(), endExternalCommands()
5060 */
5061void QQuickWindow::resetOpenGLState()
5062{
5063 Q_D(QQuickWindow);
5064
5065 if (!openglContext())
5066 return;
5067
5068 QOpenGLContext *ctx = openglContext();
5069 QOpenGLFunctions *gl = ctx->functions();
5070
5071 gl->glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
5072 gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
5073
5074 if (!d->vaoHelper)
5075 d->vaoHelper = new QOpenGLVertexArrayObjectHelper(ctx);
5076 if (d->vaoHelper->isValid())
5077 d->vaoHelper->glBindVertexArray(array: 0);
5078
5079 if (ctx->isOpenGLES() || (gl->openGLFeatures() & QOpenGLFunctions::FixedFunctionPipeline)) {
5080 int maxAttribs;
5081 gl->glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, params: &maxAttribs);
5082 for (int i=0; i<maxAttribs; ++i) {
5083 gl->glVertexAttribPointer(indx: i, size: 4, GL_FLOAT, GL_FALSE, stride: 0, ptr: nullptr);
5084 gl->glDisableVertexAttribArray(index: i);
5085 }
5086 }
5087
5088 gl->glActiveTexture(GL_TEXTURE0);
5089 gl->glBindTexture(GL_TEXTURE_2D, texture: 0);
5090
5091 gl->glDisable(GL_DEPTH_TEST);
5092 gl->glDisable(GL_STENCIL_TEST);
5093 gl->glDisable(GL_SCISSOR_TEST);
5094
5095 gl->glColorMask(red: true, green: true, blue: true, alpha: true);
5096 gl->glClearColor(red: 0, green: 0, blue: 0, alpha: 0);
5097
5098 gl->glDepthMask(flag: true);
5099 gl->glDepthFunc(GL_LESS);
5100 gl->glClearDepthf(depth: 1);
5101
5102 gl->glStencilMask(mask: 0xff);
5103 gl->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
5104 gl->glStencilFunc(GL_ALWAYS, ref: 0, mask: 0xff);
5105
5106 gl->glDisable(GL_BLEND);
5107 gl->glBlendFunc(GL_ONE, GL_ZERO);
5108
5109 gl->glUseProgram(program: 0);
5110
5111 QOpenGLFramebufferObject::bindDefault();
5112}
5113#endif
5114
5115/*!
5116 \struct QQuickWindow::GraphicsStateInfo
5117 \inmodule QtQuick
5118 \since 5.14
5119
5120 \brief Describes some of the RHI's graphics state at the point of a
5121 \l{QQuickWindow::beginExternalCommands()}{beginExternalCommands()} call.
5122 */
5123
5124/*!
5125 \return a reference to a GraphicsStateInfo struct describing some of the
5126 RHI's internal state, in particular, the double or tripple buffering status
5127 of the backend (such as, the Vulkan or Metal integrations). This is
5128 relevant when the underlying graphics APIs is Vulkan or Metal, and the
5129 external rendering code wishes to perform double or tripple buffering of
5130 its own often-changing resources, such as, uniform buffers, in order to
5131 avoid stalling the pipeline.
5132 */
5133const QQuickWindow::GraphicsStateInfo &QQuickWindow::graphicsStateInfo()
5134{
5135 Q_D(QQuickWindow);
5136 if (d->rhi) {
5137 d->rhiStateInfo.currentFrameSlot = d->rhi->currentFrameSlot();
5138 d->rhiStateInfo.framesInFlight = d->rhi->resourceLimit(limit: QRhi::FramesInFlight);
5139 }
5140 return d->rhiStateInfo;
5141}
5142
5143/*!
5144 When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene
5145 graph rendering, it is necessary to call this function before recording
5146 commands to the command buffer used by the scene graph to render its main
5147 render pass. This is to avoid clobbering state.
5148
5149 In practice this function is often called from a slot connected to the
5150 beforeRenderPassRecording() or afterRenderPassRecording() signals.
5151
5152 The function does not need to be called when recording commands to the
5153 application's own command buffer (such as, a VkCommandBuffer or
5154 MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the
5155 application, not retrieved from the scene graph). With graphics APIs where
5156 no native command buffer concept is exposed (OpenGL, Direct 3D 11),
5157 beginExternalCommands() and endExternalCommands() together provide a
5158 replacement for resetOpenGLState().
5159
5160 Calling this function and endExternalCommands() is not necessary within the
5161 \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
5162 because the scene graph performs the necessary steps implicitly for render
5163 nodes.
5164
5165 Native graphics objects (such as, graphics device, command buffer or
5166 encoder) are accessible via QSGRendererInterface::getResource().
5167
5168 \warning Watch out for the fact that
5169 QSGRendererInterface::CommandListResource may return a different object
5170 between beginExternalCommands() - endExternalCommands(). This can happen
5171 when the underlying implementation provides a dedicated secondary command
5172 buffer for recording external graphics commands within a render pass.
5173 Therefore, always query CommandListResource after calling this function. Do
5174 not attempt to reuse an object from an earlier query.
5175
5176 \note This function has no effect when the scene graph is using OpenGL
5177 directly and the RHI graphics abstraction layer is not in use. Refer to
5178 resetOpenGLState() in that case.
5179
5180 \note When the scenegraph is using the RHI graphics abstraction layer with
5181 the OpenGL backend underneath, pay attention to the fact that the OpenGL
5182 state in the context can have arbitrary settings, and this function does not
5183 perform any resetting of the state back to defaults. Call
5184 resetOpenGLState() if that is seen necessary.
5185
5186 \sa endExternalCommands(), resetOpenGLState()
5187
5188 \since 5.14
5189 */
5190void QQuickWindow::beginExternalCommands()
5191{
5192#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
5193 Q_D(QQuickWindow);
5194 if (d->rhi && d->context && d->context->isValid()) {
5195 QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context);
5196 QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
5197 if (cb)
5198 cb->beginExternal();
5199 }
5200#endif
5201}
5202
5203/*!
5204 When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene
5205 graph rendering, it is necessary to call this function after recording
5206 commands to the command buffer used by the scene graph to render its main
5207 render pass. This is to avoid clobbering state.
5208
5209 In practice this function is often called from a slot connected to the
5210 beforeRenderPassRecording() or afterRenderPassRecording() signals.
5211
5212 The function does not need to be called when recording commands to the
5213 application's own command buffer (such as, a VkCommandBuffer or
5214 MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the
5215 application, not retrieved from the scene graph). With graphics APIs where
5216 no native command buffer concept is exposed (OpenGL, Direct 3D 11),
5217 beginExternalCommands() and endExternalCommands() together provide a
5218 replacement for resetOpenGLState().
5219
5220 Calling this function and beginExternalCommands() is not necessary within the
5221 \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode
5222 because the scene graph performs the necessary steps implicitly for render
5223 nodes.
5224
5225 \note This function has no effect when the scene graph is using OpenGL
5226 directly and the RHI graphics abstraction layer is not in use. Refer to
5227 resetOpenGLState() in that case.
5228
5229 \sa beginExternalCommands()
5230
5231 \since 5.14
5232 */
5233void QQuickWindow::endExternalCommands()
5234{
5235#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
5236 Q_D(QQuickWindow);
5237 if (d->rhi && d->context && d->context->isValid()) {
5238 QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context);
5239 QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer();
5240 if (cb)
5241 cb->endExternal();
5242 }
5243#endif
5244}
5245
5246/*!
5247 \qmlproperty string Window::title
5248
5249 The window's title in the windowing system.
5250
5251 The window title might appear in the title area of the window decorations,
5252 depending on the windowing system and the window flags. It might also
5253 be used by the windowing system to identify the window in other contexts,
5254 such as in the task switcher.
5255 */
5256
5257/*!
5258 \qmlproperty Qt::WindowModality Window::modality
5259
5260 The modality of the window.
5261
5262 A modal window prevents other windows from receiving input events.
5263 Possible values are Qt.NonModal (the default), Qt.WindowModal,
5264 and Qt.ApplicationModal.
5265 */
5266
5267/*!
5268 \qmlproperty Qt::WindowFlags Window::flags
5269
5270 The window flags of the window.
5271
5272 The window flags control the window's appearance in the windowing system,
5273 whether it's a dialog, popup, or a regular window, and whether it should
5274 have a title bar, etc.
5275
5276 The flags which you read from this property might differ from the ones
5277 that you set if the requested flags could not be fulfilled.
5278
5279 \sa Qt::WindowFlags
5280 */
5281
5282/*!
5283 \qmlattachedproperty Window Window::window
5284 \since 5.7
5285
5286 This attached property holds the item's window.
5287 The Window attached property can be attached to any Item.
5288*/
5289
5290/*!
5291 \qmlattachedproperty int Window::width
5292 \qmlattachedproperty int Window::height
5293 \since 5.5
5294
5295 These attached properties hold the size of the item's window.
5296 The Window attached property can be attached to any Item.
5297*/
5298
5299/*!
5300 \qmlproperty int Window::x
5301 \qmlproperty int Window::y
5302 \qmlproperty int Window::width
5303 \qmlproperty int Window::height
5304
5305 Defines the window's position and size.
5306
5307 The (x,y) position is relative to the \l Screen if there is only one,
5308 or to the virtual desktop (arrangement of multiple screens).
5309
5310 \qml
5311 Window { x: 100; y: 100; width: 100; height: 100 }
5312 \endqml
5313
5314 \image screen-and-window-dimensions.jpg
5315 */
5316
5317/*!
5318 \qmlproperty int Window::minimumWidth
5319 \qmlproperty int Window::minimumHeight
5320 \since 5.1
5321
5322 Defines the window's minimum size.
5323
5324 This is a hint to the window manager to prevent resizing below the specified
5325 width and height.
5326 */
5327
5328/*!
5329 \qmlproperty int Window::maximumWidth
5330 \qmlproperty int Window::maximumHeight
5331 \since 5.1
5332
5333 Defines the window's maximum size.
5334
5335 This is a hint to the window manager to prevent resizing above the specified
5336 width and height.
5337 */
5338
5339/*!
5340 \qmlproperty bool Window::visible
5341
5342 Whether the window is visible on the screen.
5343
5344 Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}.
5345
5346 \sa visibility
5347 */
5348
5349/*!
5350 \qmlproperty QWindow::Visibility Window::visibility
5351
5352 The screen-occupation state of the window.
5353
5354 Visibility is whether the window should appear in the windowing system as
5355 normal, minimized, maximized, fullscreen or hidden.
5356
5357 To set the visibility to \l {QWindow::}{AutomaticVisibility} means to give the
5358 window a default visible state, which might be \l {QWindow::}{FullScreen} or
5359 \l {QWindow::}{Windowed} depending on the platform. However when reading the
5360 visibility property you will always get the actual state, never
5361 \c AutomaticVisibility.
5362
5363 When a window is not visible its visibility is Hidden, and setting
5364 visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false.
5365
5366 \sa visible
5367 \since 5.1
5368 */
5369
5370/*!
5371 \qmlattachedproperty QWindow::Visibility Window::visibility
5372 \since 5.4
5373
5374 This attached property holds whether the window is currently shown
5375 in the windowing system as normal, minimized, maximized, fullscreen or
5376 hidden. The \c Window attached property can be attached to any Item. If the
5377 item is not shown in any window, the value will be \l {QWindow::}{Hidden}.
5378
5379 \sa visible, visibility
5380*/
5381
5382/*!
5383 \qmlproperty Item Window::contentItem
5384 \readonly
5385 \brief The invisible root item of the scene.
5386*/
5387
5388/*!
5389 \qmlproperty Qt::ScreenOrientation Window::contentOrientation
5390
5391 This is a hint to the window manager in case it needs to display
5392 additional content like popups, dialogs, status bars, or similar
5393 in relation to the window.
5394
5395 The recommended orientation is \l {Screen::orientation}{Screen.orientation}, but
5396 an application doesn't have to support all possible orientations,
5397 and thus can opt to ignore the current screen orientation.
5398
5399 The difference between the window and the content orientation
5400 determines how much to rotate the content by.
5401
5402 The default value is Qt::PrimaryOrientation.
5403
5404 \sa Screen
5405
5406 \since 5.1
5407 */
5408
5409/*!
5410 \qmlproperty real Window::opacity
5411
5412 The opacity of the window.
5413
5414 If the windowing system supports window opacity, this can be used to fade the
5415 window in and out, or to make it semitransparent.
5416
5417 A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below
5418 is treated as fully transparent. Values inbetween represent varying levels of
5419 translucency between the two extremes.
5420
5421 The default value is 1.0.
5422
5423 \since 5.1
5424 */
5425
5426/*!
5427 \qmlproperty variant Window::screen
5428
5429 The screen with which the window is associated.
5430
5431 If specified before showing a window, will result in the window being shown
5432 on that screen, unless an explicit window position has been set. The value
5433 must be an element from the Qt.application.screens array.
5434
5435 \note To ensure that the window is associated with the desired screen when
5436 the underlying native window is created, make sure this property is set as
5437 early as possible and that the setting of its value is not deferred. This
5438 can be particularly important on embedded platforms without a windowing system,
5439 where only one window per screen is allowed at a time. Setting the screen after
5440 a window has been created does not move the window if the new screen is part of
5441 the same virtual desktop as the old screen.
5442
5443 \since 5.9
5444
5445 \sa QWindow::setScreen(), QWindow::screen(), QScreen, {QtQml::Qt::application}{Qt.application}
5446 */
5447
5448/*!
5449 \qmlproperty QWindow Window::transientParent
5450 \since 5.13
5451
5452 The window for which this window is a transient pop-up.
5453
5454 This is a hint to the window manager that this window is a dialog or pop-up
5455 on behalf of the transient parent. It usually means that the transient
5456 window will be centered over its transient parent when it is initially
5457 shown, that minimizing the parent window will also minimize the transient
5458 window, and so on; however results vary somewhat from platform to platform.
5459
5460 Normally if you declare a Window inside an Item or inside another Window,
5461 this relationship is deduced automatically. In that case, if you declare
5462 this window's \l visible property \c true, it will not actually be shown
5463 until the \c transientParent window is shown.
5464
5465 However if you set this property, then Qt Quick will no longer wait until
5466 the \c transientParent window is shown before showing this window. If you
5467 want to to be able to show a transient window independently of the "parent"
5468 Item or Window within which it was declared, you can remove that
5469 relationship by setting \c transientParent to \c null:
5470
5471 \qml
5472 import QtQuick.Window 2.13
5473
5474 Window {
5475 // visible is false by default
5476 Window {
5477 transientParent: null
5478 visible: true
5479 }
5480 }
5481 \endqml
5482
5483 In order to cause the window to be centered above its transient parent by
5484 default, depending on the window manager, it may also be necessary to set
5485 the \l Window::flags property with a suitable \l Qt::WindowType (such as
5486 \c Qt::Dialog).
5487*/
5488
5489/*!
5490 \property QQuickWindow::transientParent
5491 \brief The window for which this window is a transient pop-up.
5492 \since 5.13
5493
5494 This is a hint to the window manager that this window is a dialog or pop-up
5495 on behalf of the transient parent, which may be any kind of \l QWindow.
5496
5497 In order to cause the window to be centered above its transient parent by
5498 default, depending on the window manager, it may also be necessary to set
5499 the \l flags property with a suitable \l Qt::WindowType (such as \c Qt::Dialog).
5500
5501 \sa parent()
5502 */
5503
5504/*!
5505 \qmlproperty Item Window::activeFocusItem
5506 \since 5.1
5507
5508 The item which currently has active focus or \c null if there is
5509 no item with active focus.
5510 */
5511
5512/*!
5513 \qmlattachedproperty Item Window::activeFocusItem
5514 \since 5.4
5515
5516 This attached property holds the item which currently has active focus or
5517 \c null if there is no item with active focus. The Window attached property
5518 can be attached to any Item.
5519*/
5520
5521/*!
5522 \qmlproperty bool Window::active
5523 \since 5.1
5524
5525 The active status of the window.
5526
5527 \sa requestActivate()
5528 */
5529
5530/*!
5531 \qmlattachedproperty bool Window::active
5532 \since 5.4
5533
5534 This attached property tells whether the window is active. The Window
5535 attached property can be attached to any Item.
5536
5537 Here is an example which changes a label to show the active state of the
5538 window in which it is shown:
5539
5540 \qml
5541 import QtQuick 2.4
5542 import QtQuick.Window 2.2
5543
5544 Text {
5545 text: Window.active ? "active" : "inactive"
5546 }
5547 \endqml
5548*/
5549
5550/*!
5551 \qmlmethod QtQuick::Window::requestActivate()
5552 \since 5.1
5553
5554 Requests the window to be activated, i.e. receive keyboard focus.
5555 */
5556
5557/*!
5558 \qmlmethod QtQuick::Window::alert(int msec)
5559 \since 5.1
5560
5561 Causes an alert to be shown for \a msec milliseconds. If \a msec is \c 0
5562 (the default), then the alert is shown indefinitely until the window
5563 becomes active again.
5564
5565 In alert state, the window indicates that it demands attention, for example
5566 by flashing or bouncing the taskbar entry.
5567*/
5568
5569/*!
5570 \qmlmethod QtQuick::Window::close()
5571
5572 Closes the window.
5573
5574 When this method is called, or when the user tries to close the window by
5575 its title bar button, the \l closing signal will be emitted. If there is no
5576 handler, or the handler does not revoke permission to close, the window
5577 will subsequently close. If the QGuiApplication::quitOnLastWindowClosed
5578 property is \c true, and there are no other windows open, the application
5579 will quit.
5580*/
5581
5582/*!
5583 \qmlmethod QtQuick::Window::raise()
5584
5585 Raises the window in the windowing system.
5586
5587 Requests that the window be raised to appear above other windows.
5588*/
5589
5590/*!
5591 \qmlmethod QtQuick::Window::lower()
5592
5593 Lowers the window in the windowing system.
5594
5595 Requests that the window be lowered to appear below other windows.
5596*/
5597
5598/*!
5599 \qmlmethod QtQuick::Window::show()
5600
5601 Shows the window.
5602
5603 This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(),
5604 depending on the platform's default behavior for the window type and flags.
5605
5606 \sa showFullScreen(), showMaximized(), showNormal(), hide(), QQuickItem::flags()
5607*/
5608
5609/*!
5610 \qmlmethod QtQuick::Window::hide()
5611
5612 Hides the window.
5613
5614 Equivalent to setting \l visible to \c false or \l visibility to \l {QWindow::}{Hidden}.
5615
5616 \sa show()
5617*/
5618
5619/*!
5620 \qmlmethod QtQuick::Window::showMinimized()
5621
5622 Shows the window as minimized.
5623
5624 Equivalent to setting \l visibility to \l {QWindow::}{Minimized}.
5625*/
5626
5627/*!
5628 \qmlmethod QtQuick::Window::showMaximized()
5629
5630 Shows the window as maximized.
5631
5632 Equivalent to setting \l visibility to \l {QWindow::}{Maximized}.
5633*/
5634
5635/*!
5636 \qmlmethod QtQuick::Window::showFullScreen()
5637
5638 Shows the window as fullscreen.
5639
5640 Equivalent to setting \l visibility to \l {QWindow::}{FullScreen}.
5641*/
5642
5643/*!
5644 \qmlmethod QtQuick::Window::showNormal()
5645
5646 Shows the window as normal, i.e. neither maximized, minimized, nor fullscreen.
5647
5648 Equivalent to setting \l visibility to \l {QWindow::}{Windowed}.
5649*/
5650
5651/*!
5652 \enum QQuickWindow::RenderStage
5653 \since 5.4
5654
5655 \value BeforeSynchronizingStage Before synchronization.
5656 \value AfterSynchronizingStage After synchronization.
5657 \value BeforeRenderingStage Before rendering.
5658 \value AfterRenderingStage After rendering.
5659 \value AfterSwapStage After the frame is swapped.
5660 \value NoStage As soon as possible. This value was added in Qt 5.6.
5661
5662 \sa {Scene Graph and Rendering}
5663 */
5664
5665/*!
5666 \since 5.4
5667
5668 Schedules \a job to run when the rendering of this window reaches
5669 the given \a stage.
5670
5671 This is a convenience to the equivalent signals in QQuickWindow for
5672 "one shot" tasks.
5673
5674 The window takes ownership over \a job and will delete it when the
5675 job is completed.
5676
5677 If rendering is shut down before \a job has a chance to run, the
5678 job will be run and then deleted as part of the scene graph cleanup.
5679 If the window is never shown and no rendering happens before the QQuickWindow
5680 is destroyed, all pending jobs will be destroyed without their run()
5681 method being called.
5682
5683 If the rendering is happening on a different thread, then the job
5684 will happen on the rendering thread.
5685
5686 If \a stage is \l NoStage, \a job will be run at the earliest opportunity
5687 whenever the render thread is not busy rendering a frame. If there is no
5688 OpenGL context available or the window is not exposed at the time the job is
5689 either posted or handled, it is deleted without executing the run() method.
5690 If a non-threaded renderer is in use, the run() method of the job is executed
5691 synchronously.
5692 The OpenGL context is changed to the renderer context before executing a
5693 \l NoStage job.
5694
5695 \note This function does not trigger rendering; the jobs targeting any other
5696 stage than NoStage will be stored run until rendering is triggered elsewhere.
5697 To force the job to run earlier, call QQuickWindow::update();
5698
5699 \sa beforeRendering(), afterRendering(), beforeSynchronizing(),
5700 afterSynchronizing(), frameSwapped(), sceneGraphInvalidated()
5701 */
5702
5703void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage)
5704{
5705 Q_D(QQuickWindow);
5706
5707 d->renderJobMutex.lock();
5708 if (stage == BeforeSynchronizingStage) {
5709 d->beforeSynchronizingJobs << job;
5710 } else if (stage == AfterSynchronizingStage) {
5711 d->afterSynchronizingJobs << job;
5712 } else if (stage == BeforeRenderingStage) {
5713 d->beforeRenderingJobs << job;
5714 } else if (stage == AfterRenderingStage) {
5715 d->afterRenderingJobs << job;
5716 } else if (stage == AfterSwapStage) {
5717 d->afterSwapJobs << job;
5718 } else if (stage == NoStage) {
5719 if (d->renderControl && openglContext()
5720#if QT_CONFIG(opengl)
5721 && openglContext()->thread() == QThread::currentThread()
5722#endif
5723 ) {
5724 job->run();
5725 delete job;
5726 } else if (isExposed()) {
5727 d->windowManager->postJob(window: this, job);
5728 } else {
5729 delete job;
5730 }
5731 }
5732 d->renderJobMutex.unlock();
5733}
5734
5735void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs)
5736{
5737 renderJobMutex.lock();
5738 QList<QRunnable *> jobList = *jobs;
5739 jobs->clear();
5740 renderJobMutex.unlock();
5741
5742 for (QRunnable *r : qAsConst(t&: jobList)) {
5743 r->run();
5744 delete r;
5745 }
5746}
5747
5748void QQuickWindow::runJobsAfterSwap()
5749{
5750 Q_D(QQuickWindow);
5751 d->runAndClearJobs(jobs: &d->afterSwapJobs);
5752}
5753
5754/*!
5755 * Returns the device pixel ratio for this window.
5756 *
5757 * This is different from QWindow::devicePixelRatio() in that it supports
5758 * redirected rendering via QQuickRenderControl. When using a
5759 * QQuickRenderControl, the QQuickWindow is often not created, meaning it is
5760 * never shown and there is no underlying native window created in the
5761 * windowing system. As a result, querying properties like the device pixel
5762 * ratio cannot give correct results. Use this function instead.
5763 *
5764 * \sa QWindow::devicePixelRatio()
5765 */
5766qreal QQuickWindow::effectiveDevicePixelRatio() const
5767{
5768 QWindow *w = QQuickRenderControl::renderWindowFor(win: const_cast<QQuickWindow *>(this));
5769 return w ? w->devicePixelRatio() : devicePixelRatio();
5770}
5771
5772/*!
5773 \return the current renderer interface. The value is always valid and is never null.
5774
5775 \note This function can be called at any time after constructing the
5776 QQuickWindow, even while isSceneGraphInitialized() is still false. However,
5777 some renderer interface functions, in particular
5778 QSGRendererInterface::getResource() will not be functional until the
5779 scenegraph is up and running. Backend queries, like
5780 QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(),
5781 will always be functional on the other hand.
5782
5783 \note The ownership of the returned pointer stays with Qt. The returned
5784 instance may or may not be shared between different QQuickWindow instances,
5785 depending on the scenegraph backend in use. Therefore applications are
5786 expected to query the interface object for each QQuickWindow instead of
5787 reusing the already queried pointer.
5788
5789 \sa QSGRenderNode, QSGRendererInterface
5790
5791 \since 5.8
5792 */
5793QSGRendererInterface *QQuickWindow::rendererInterface() const
5794{
5795 Q_D(const QQuickWindow);
5796
5797 // no context validity check - it is essential to be able to return a
5798 // renderer interface instance before scenegraphInitialized() is emitted
5799 // (depending on the backend, that can happen way too late for some of the
5800 // rif use cases, like examining the graphics api or shading language in
5801 // use)
5802
5803 return d->context->sceneGraphContext()->rendererInterface(renderContext: d->context);
5804}
5805
5806/*!
5807 Requests a Qt Quick scenegraph backend for the specified graphics \a api.
5808 Backends can either be built-in or be installed in form of dynamically
5809 loaded plugins.
5810
5811 \note The call to the function must happen before constructing the first
5812 QQuickWindow in the application. It cannot be changed afterwards.
5813
5814 If the selected backend is invalid or an error occurs, the default backend
5815 (OpenGL or software, depending on the Qt configuration) is used.
5816
5817 \since 5.8
5818 */
5819void QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi api)
5820{
5821 switch (api) {
5822 case QSGRendererInterface::Software:
5823 setSceneGraphBackend(QStringLiteral("software"));
5824 break;
5825 case QSGRendererInterface::Direct3D12:
5826 setSceneGraphBackend(QStringLiteral("d3d12"));
5827 break;
5828 default:
5829 break;
5830 }
5831#if QT_CONFIG(opengl) /* || QT_CONFIG(vulkan) || defined(Q_OS_WIN) || defined(Q_OS_DARWIN) */
5832 if (QSGRendererInterface::isApiRhiBased(api))
5833 QSGRhiSupport::configure(api);
5834#endif
5835}
5836
5837/*!
5838 Requests the specified Qt Quick scenegraph \a backend. Backends can either
5839 be built-in or be installed in form of dynamically loaded plugins.
5840
5841 \overload
5842
5843 \note The call to the function must happen before constructing the first
5844 QQuickWindow in the application. It cannot be changed afterwards.
5845
5846 If \a backend is invalid or an error occurs, the default backend (OpenGL or
5847 software, depending on the Qt configuration) is used.
5848
5849 \note Calling this function is equivalent to setting the
5850 \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this
5851 API is safer to use in applications that spawn other processes as there is
5852 no need to worry about environment inheritance.
5853
5854 \since 5.8
5855 */
5856void QQuickWindow::setSceneGraphBackend(const QString &backend)
5857{
5858 QSGContext::setBackend(backend);
5859}
5860
5861/*!
5862 Returns the requested Qt Quick scenegraph backend.
5863
5864 \note The return value of this function may still be outdated by
5865 subsequent calls to setSceneGraphBackend() until the first QQuickWindow in the
5866 application has been constructed.
5867
5868 \since 5.9
5869 */
5870QString QQuickWindow::sceneGraphBackend()
5871{
5872 return QSGContext::backend();
5873}
5874
5875/*!
5876 Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
5877
5878 This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
5879
5880 \since 5.8
5881 \sa QSGRectangleNode
5882 */
5883QSGRectangleNode *QQuickWindow::createRectangleNode() const
5884{
5885 Q_D(const QQuickWindow);
5886 return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr;
5887}
5888
5889/*!
5890 Creates a simple image node. When the scenegraph is not initialized, the return value is null.
5891
5892 This is cross-backend alternative to constructing a QSGSimpleTextureNode directly.
5893
5894 \since 5.8
5895 \sa QSGImageNode
5896 */
5897QSGImageNode *QQuickWindow::createImageNode() const
5898{
5899 Q_D(const QQuickWindow);
5900 return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr;
5901}
5902
5903/*!
5904 Creates a nine patch node. When the scenegraph is not initialized, the return value is null.
5905
5906 \since 5.8
5907 */
5908QSGNinePatchNode *QQuickWindow::createNinePatchNode() const
5909{
5910 Q_D(const QQuickWindow);
5911 return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr;
5912}
5913
5914/*!
5915 \since 5.10
5916
5917 Returns the render type of text-like elements in Qt Quick.
5918 The default is QQuickWindow::QtTextRendering.
5919
5920 \sa setTextRenderType()
5921*/
5922QQuickWindow::TextRenderType QQuickWindow::textRenderType()
5923{
5924 return QQuickWindowPrivate::textRenderType;
5925}
5926
5927/*!
5928 \since 5.10
5929
5930 Sets the default render type of text-like elements in Qt Quick to \a renderType.
5931
5932 \note setting the render type will only affect elements created afterwards;
5933 the render type of existing elements will not be modified.
5934
5935 \sa textRenderType()
5936*/
5937void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType)
5938{
5939 QQuickWindowPrivate::textRenderType = renderType;
5940}
5941
5942#ifndef QT_NO_DEBUG_STREAM
5943QDebug operator<<(QDebug debug, const QQuickWindow *win)
5944{
5945 QDebugStateSaver saver(debug);
5946 debug.nospace();
5947 if (!win) {
5948 debug << "QQuickWindow(0)";
5949 return debug;
5950 }
5951
5952 debug << win->metaObject()->className() << '(' << static_cast<const void *>(win);
5953 if (win->isActive())
5954 debug << " active";
5955 if (win->isExposed())
5956 debug << " exposed";
5957 debug << ", visibility=" << win->visibility() << ", flags=" << win->flags();
5958 if (!win->title().isEmpty())
5959 debug << ", title=" << win->title();
5960 if (!win->objectName().isEmpty())
5961 debug << ", name=" << win->objectName();
5962 if (win->parent())
5963 debug << ", parent=" << static_cast<const void *>(win->parent());
5964 if (win->transientParent())
5965 debug << ", transientParent=" << static_cast<const void *>(win->transientParent());
5966 debug << ", geometry=";
5967 QtDebugUtils::formatQRect(debug, rect: win->geometry());
5968 debug << ')';
5969 return debug;
5970}
5971#endif
5972
5973#include "moc_qquickwindow.cpp"
5974
5975QT_END_NAMESPACE
5976

source code of qtdeclarative/src/quick/items/qquickwindow.cpp