Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickwidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickwidget.h"
7#include <QtWidgets/private/qwidgetrepaintmanager_p.h>
8
9#include "private/qquickwindow_p.h"
10#include "private/qquickitem_p.h"
11#include "private/qquickitemchangelistener_p.h"
12#include "private/qquickrendercontrol_p.h"
13#include "private/qsgrhisupport_p.h"
14
15#include "private/qsgsoftwarerenderer_p.h"
16
17#include <private/qqmldebugconnector_p.h>
18#include <private/qquickprofiler_p.h>
19#include <private/qqmldebugserviceinterfaces_p.h>
20
21#include <QtQml/qqmlengine.h>
22#include <QtQml/qqmlcomponent.h>
23#include <private/qqmlengine_p.h>
24#include <QtCore/qbasictimer.h>
25#include <QtGui/QOffscreenSurface>
26#include <QtGui/private/qguiapplication_p.h>
27#include <QtGui/qpa/qplatformintegration.h>
28
29#include <QtGui/QPainter>
30
31#include <QtQuick/QSGRendererInterface>
32
33#ifdef Q_OS_WIN
34#if QT_CONFIG(messagebox)
35# include <QtWidgets/QMessageBox>
36#endif
37# include <QtCore/QLibraryInfo>
38# include <QtCore/qt_windows.h>
39#endif
40
41#include <QtQuick/qquickgraphicsdevice.h>
42#include <QtQuick/qquickrendertarget.h>
43
44#include "private/qwidget_p.h"
45
46#if QT_CONFIG(graphicsview)
47#include <QtWidgets/qgraphicsscene.h>
48#include <QtWidgets/qgraphicsview.h>
49#endif
50
51#if QT_CONFIG(vulkan)
52#include <QtGui/private/qvulkandefaultinstance_p.h>
53#endif
54
55QT_BEGIN_NAMESPACE
56
57QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control)
58:QQuickWindow(dd, control)
59{
60 setTitle(QString::fromLatin1("Offscreen"));
61 setObjectName(QString::fromLatin1("QQuickWidgetOffscreenWindow"));
62}
63
64// override setVisble to prevent accidental offscreen window being created
65// by base class.
67public:
68 void setVisible(bool visible) override {
69 Q_Q(QWindow);
70 // this stays always invisible
71 visibility = visible ? QWindow::Windowed : QWindow::Hidden;
72 q->visibilityChanged(visibility); // workaround for QTBUG-49054
73 }
74};
75
77
86
88{
89public:
90 Q_DECLARE_PUBLIC(QQuickWidgetRenderControl)
96
98#if QT_CONFIG(graphicsview)
100 auto *proxy = (widgetd && widgetd->extra) ? widgetd->extra->proxyWidget : nullptr;
101 auto *scene = proxy ? proxy->scene() : nullptr;
102 if (scene) {
103 for (const auto &view : scene->views()) {
104 if (view->window()->windowHandle() == w)
105 return true;
106 }
107 }
108
109 return m_quickWidget->window()->windowHandle() == w;
110#endif
111 }
113};
114
115QQuickWidgetRenderControl::QQuickWidgetRenderControl(QQuickWidget *quickWidget)
116 : QQuickRenderControl(*(new QQuickWidgetRenderControlPrivate(this, quickWidget)), nullptr)
117{
118}
119
121{
122 Q_D(QQuickWidgetRenderControl);
123 if (offset)
124 *offset = d->m_quickWidget->mapTo(d->m_quickWidget->window(), QPoint());
125
126 QWindow *result = nullptr;
127#if QT_CONFIG(graphicsview)
128 QWidgetPrivate *widgetd = QWidgetPrivate::get(d->m_quickWidget);
129 if (widgetd->extra) {
130 if (auto proxy = widgetd->extra->proxyWidget) {
131 auto scene = proxy->scene();
132 if (scene) {
133 const auto views = scene->views();
134 if (!views.isEmpty()) {
135 // Get the first QGV containing the proxy. Not ideal, but the callers
136 // of this function aren't prepared to handle more than one render window.
137 auto candidateView = views.first();
138 result = candidateView->window()->windowHandle();
139 }
140 }
141 }
142 }
143#endif
144 if (!result)
145 result = d->m_quickWidget->window()->windowHandle();
146
147 return result;
148}
149
151{
152 Q_Q(QQuickWidget);
153
155 offscreenWindow->setScreen(q->screen());
156 // Do not call create() on offscreenWindow.
157
158 QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject()));
159 QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject()));
160 QWidget::connect(offscreenWindow, &QQuickWindow::focusObjectChanged, q, &QQuickWidget::propagateFocusObjectChanged);
161
162#if QT_CONFIG(accessibility)
163 QAccessible::installFactory(&qAccessibleQuickWidgetFactory);
164#endif
165}
166
168{
169 // This should initialize, if not already done, the absolute minimum set of
170 // mandatory backing resources, meaning the QQuickWindow and its
171 // QQuickRenderControl. This function may be called very early on upon
172 // construction, including before init() even.
173
174 Q_Q(QQuickWidget);
175 if (!renderControl)
176 renderControl = new QQuickWidgetRenderControl(q);
177 if (!offscreenWindow) {
178 offscreenWindow = new QQuickWidgetOffscreenWindow(*new QQuickWidgetOffscreenWindowPrivate(), renderControl);
179 offscreenWindow->setProperty("_q_parentWidget", QVariant::fromValue(q));
180 }
181
182 // Check if the Software Adaptation is being used
183 auto sgRendererInterface = offscreenWindow->rendererInterface();
184 if (sgRendererInterface && sgRendererInterface->graphicsApi() == QSGRendererInterface::Software)
185 useSoftwareRenderer = true;
186}
187
188void QQuickWidgetPrivate::init(QQmlEngine* e)
189{
190 Q_Q(QQuickWidget);
191
193
194 if (!useSoftwareRenderer) {
195 if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering))
196 setRenderToTexture();
197 else
198 qWarning("QQuickWidget is not supported on this platform.");
199 }
200
201 engine = e;
202
203 if (!engine.isNull() && !engine.data()->incubationController())
204 engine.data()->setIncubationController(offscreenWindow->incubationController());
205
206 q->setMouseTracking(true);
207 q->setFocusPolicy(Qt::StrongFocus);
208#ifndef Q_OS_MACOS
209 /*
210 Usually, a QTouchEvent comes from a touchscreen, and we want those
211 touch events in Qt Quick. But on macOS, there are no touchscreens, and
212 WA_AcceptTouchEvents has a different meaning: QApplication::notify()
213 calls the native-integration function registertouchwindow() to change
214 NSView::allowedTouchTypes to include NSTouchTypeMaskIndirect when the
215 trackpad cursor enters the window, and removes that mask when the
216 cursor exits. In other words, WA_AcceptTouchEvents enables getting
217 discrete touchpoints from the trackpad. We rather prefer to get mouse,
218 wheel and native gesture events from the trackpad (because those
219 provide more of a "native feel"). The only exception is for
220 MultiPointTouchArea, and it takes care of that for itself. So don't
221 automatically set WA_AcceptTouchEvents on macOS. The user can still do
222 it, but we don't recommend it.
223 */
224 q->setAttribute(Qt::WA_AcceptTouchEvents);
225#endif
226
227#if QT_CONFIG(quick_draganddrop)
228 q->setAcceptDrops(true);
229#endif
230
231 QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate()));
232 QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate()));
233}
234
236{
237 Q_Q(const QQuickWidget);
238 if (!engine.isNull())
239 return;
240
241 engine = new QQmlEngine(const_cast<QQuickWidget*>(q));
242 engine.data()->setIncubationController(offscreenWindow->incubationController());
243}
244
246{
247 if (!useSoftwareRenderer && rhi) {
248 // For the user's own OpenGL code connected to some QQuickWindow signals.
249 rhi->makeThreadLocalNativeContextCurrent();
250 }
251
252 renderControl->invalidate();
253}
254
256{
257 Q_Q(QQuickWidget);
258 if (rhi)
259 rhi->removeCleanupCallback(q);
260
262 deviceLost = true;
263 rhi = nullptr;
264}
265
267{
268 Q_Q(QQuickWidget);
269
270 if (offscreenWindow->isPersistentSceneGraph()
271 && qGuiApp->testAttribute(Qt::AA_ShareOpenGLContexts)
272 && rhiConfig().api() == QPlatformBackingStoreRhiConfig::OpenGL)
273 {
274 return;
275 }
276
277 // In case of !isPersistentSceneGraph or when we need a new context due to
278 // the need to share resources with the new window's context, we must both
279 // invalidate the scenegraph and destroy the context. QQuickRenderControl
280 // must be recreated because its RHI will contain a dangling pointer to
281 // the context.
282
283 QScopedPointer<QQuickWindow> oldOffScreenWindow(offscreenWindow); // Do not delete before reparenting sgItem
284 offscreenWindow = nullptr;
285 delete renderControl;
286
287 renderControl = new QQuickWidgetRenderControl(q);
289
290 if (oldOffScreenWindow)
291 offscreenWindow->setColor(oldOffScreenWindow->color());
292
293 QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate()));
294 QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate()));
295
296 if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(root))
297 sgItem->setParentItem(offscreenWindow->contentItem());
298}
299
300QQuickWidgetPrivate::QQuickWidgetPrivate()
301 : root(nullptr)
302 , component(nullptr)
303 , offscreenWindow(nullptr)
304 , renderControl(nullptr)
305 , rhi(nullptr)
306 , outputTexture(nullptr)
307 , depthStencil(nullptr)
308 , msaaBuffer(nullptr)
309 , rt(nullptr)
310 , rtRp(nullptr)
311 , resizeMode(QQuickWidget::SizeViewToRootObject)
312 , initialSize(0,0)
313 , eventPending(false)
314 , updatePending(false)
315 , fakeHidden(false)
317 , useSoftwareRenderer(false)
318 , forceFullUpdate(false)
319 , deviceLost(false)
320{
321}
322
324{
326
327 if (root) {
328 delete root;
329 root = nullptr;
330 }
331 if (component) {
332 delete component;
333 component = nullptr;
334 }
335}
336
338{
339 Q_Q(QQuickWidget);
341 q->destroyFramebufferObject();
342 delete offscreenWindow;
343 delete renderControl;
344 offscreenRenderer.reset();
345}
346
348{
349 Q_Q(QQuickWidget);
351
352 if (!source.isEmpty()) {
353 component = new QQmlComponent(engine.data(), source, q);
354 if (!component->isLoading()) {
355 q->continueExecute();
356 } else {
357 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
358 q, SLOT(continueExecute()));
359 }
360 }
361}
362
363void QQuickWidgetPrivate::execute(QAnyStringView uri, QAnyStringView typeName)
364{
365 Q_Q(QQuickWidget);
367
368 component = new QQmlComponent(engine.data(), uri, typeName, q);
369 if (!component->isLoading()) {
370 q->continueExecute();
371 } else {
372 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
373 q, SLOT(continueExecute()));
374 }
375}
376
377void QQuickWidgetPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
378 const QRectF &oldGeometry)
379{
380 Q_Q(QQuickWidget);
381 if (resizeItem == root && resizeMode == QQuickWidget::SizeViewToRootObject) {
382 // wait for both width and height to be changed
383 resizetimer.start(0,q);
384 }
385 QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, oldGeometry);
386}
387
388void QQuickWidgetPrivate::render(bool needsSync)
389{
390 Q_Q(QQuickWidget);
391 if (!useSoftwareRenderer) {
392 if (deviceLost) {
393 deviceLost = false;
395 q->createFramebufferObject();
396 }
397
398 if (!rhi) {
399 qWarning("QQuickWidget: Attempted to render scene with no rhi");
400 return;
401 }
402
403 // createFramebufferObject() bails out when the size is empty. In this case
404 // we cannot render either.
405 if (!outputTexture)
406 return;
407
408 renderControl->beginFrame();
409 QQuickRenderControlPrivate::FrameStatus frameStatus = QQuickRenderControlPrivate::get(renderControl)->frameStatus;
410 if (frameStatus == QQuickRenderControlPrivate::DeviceLostInBeginFrame) {
411 // graphics resources controlled by us must be released
413 // skip this round and hope that the tlw's repaint manager will manage to reinitialize
414 deviceLost = true;
415 return;
416 }
417 if (frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
418 qWarning("QQuickWidget: Failed to begin recording a frame");
419 return;
420 }
421
422 if (needsSync) {
423 renderControl->polishItems();
424 renderControl->sync();
425 }
426
427 renderControl->render();
428
429 renderControl->endFrame();
430 } else {
431 //Software Renderer
432 if (needsSync) {
433 renderControl->polishItems();
434 renderControl->sync();
435 }
436 if (!offscreenWindow)
437 return;
438 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(offscreenWindow);
439 auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer);
440 if (softwareRenderer && !softwareImage.isNull()) {
441 softwareRenderer->setCurrentPaintDevice(&softwareImage);
442 if (forceFullUpdate) {
443 softwareRenderer->markDirty();
444 forceFullUpdate = false;
445 }
446 renderControl->render();
447
448 updateRegion += softwareRenderer->flushRegion();
449 }
450 }
451}
452
454{
455 Q_Q(QQuickWidget);
456 updatePending = false;
457
458 if (!q->isVisible() || fakeHidden)
459 return;
460
461 render(true);
462
463#if QT_CONFIG(graphicsview)
464 if (q->window()->graphicsProxyWidget())
465 QWidgetPrivate::nearestGraphicsProxyWidget(q)->update();
466 else
467#endif
468 {
469 if (!useSoftwareRenderer)
470 q->update(); // schedule composition
471 else if (!updateRegion.isEmpty())
472 q->update(updateRegion);
473 }
474}
475
477{
478 if (!useSoftwareRenderer && !rhi)
479 return QImage();
480
481 // grabWindow() does not work for the rhi case, we are in control of the
482 // render target, and so it is up to us to read it back. When the software
483 // renderer is in use, just call grabWindow().
484
485 if (outputTexture) {
486 render(true);
487 QRhiCommandBuffer *cb = nullptr;
488 rhi->beginOffscreenFrame(&cb);
489 QRhiResourceUpdateBatch *resUpd = rhi->nextResourceUpdateBatch();
490 QRhiReadbackResult readResult;
491 resUpd->readBackTexture(QRhiReadbackDescription(outputTexture), &readResult);
492 cb->resourceUpdate(resUpd);
493 rhi->endOffscreenFrame();
494 if (!readResult.data.isEmpty()) {
495 QImage wrapperImage(reinterpret_cast<const uchar *>(readResult.data.constData()),
496 readResult.pixelSize.width(), readResult.pixelSize.height(),
497 QImage::Format_RGBA8888_Premultiplied);
498 if (rhi->isYUpInFramebuffer())
499 return wrapperImage.flipped();
500 else
501 return wrapperImage.copy();
502 }
503 return QImage();
504 }
505
506 return offscreenWindow->grabWindow();
507}
508
509// Intentionally not overriding the QQuickWindow's focusObject.
510// Key events should go to our key event handlers, and then to the
511// QQuickWindow, not any in-scene item.
512
513/*!
514 \module QtQuickWidgets
515 \title Qt Quick Widgets C++ Classes
516 \ingroup modules
517 \brief The C++ API provided by the Qt Quick Widgets module.
518 \qtcmakepackage QuickWidgets
519 \qtvariable quickwidgets
520
521 To link against the module, add this line to your \l qmake
522 \c .pro file:
523
524 \code
525 QT += quickwidgets
526 \endcode
527
528 For more information, see the QQuickWidget class documentation.
529*/
530
531/*!
532 \class QQuickWidget
533 \since 5.3
534 \brief The QQuickWidget class provides a widget for displaying a Qt Quick user interface.
535
536 \inmodule QtQuickWidgets
537
538 This is a convenience wrapper for QQuickWindow which will automatically load and display a QML
539 scene when given the URL of the main source file. Alternatively, you can instantiate your own
540 objects using QQmlComponent and place them in a manually set up QQuickWidget.
541
542 Typical usage:
543
544 \code
545 QQuickWidget *view = new QQuickWidget;
546 view->setSource(QUrl::fromLocalFile("myqmlfile.qml"));
547 view->show();
548 \endcode
549
550 To receive errors related to loading and executing QML with QQuickWidget,
551 you can connect to the statusChanged() signal and monitor for QQuickWidget::Error.
552 The errors are available via QQuickWidget::errors().
553
554 QQuickWidget also manages sizing of the view and root object. By default, the \l resizeMode
555 is SizeViewToRootObject, which will load the component and resize it to the
556 size of the view. Alternatively the resizeMode may be set to SizeRootObjectToView which
557 will resize the view to the size of the root object.
558
559 \section1 Performance Considerations
560
561 QQuickWidget is an alternative to using QQuickView and QWidget::createWindowContainer().
562 The restrictions on stacking order do not apply, making QQuickWidget the more flexible
563 alternative, behaving more like an ordinary widget.
564
565 However, the above mentioned advantages come at the expense of performance:
566 \list
567
568 \li Unlike QQuickWindow and QQuickView, QQuickWidget involves at least one
569 additional render pass targeting an offscreen color buffer, typically a 2D
570 texture, followed by drawing a texture quad. This means increased load
571 especially for the fragment processing of the GPU.
572
573 \li Using QQuickWidget disables the \l{threaded_render_loop}{threaded render loop} on all
574 platforms. This means that some of the benefits of threaded rendering, for example
575 \l Animator classes and vsync driven animations, will not be available.
576 \endlist
577
578 \note Avoid calling winId() on a QQuickWidget. This function triggers the creation of
579 a native window, resulting in reduced performance and possibly rendering glitches. The
580 entire purpose of QQuickWidget is to render Quick scenes without a separate native
581 window, hence making it a native widget should always be avoided.
582
583 \section1 Graphics API Support
584
585 QQuickWidget is functional with all the 3D graphics APIs supported by Qt
586 Quick, as well as the \c software backend. Other backends, for example
587 OpenVG, are not compatible however and attempting to construct a
588 QQuickWidget will lead to problems.
589
590 Overriding the platform's default graphics API is done the same way as with
591 QQuickWindow and QQuickView: either by calling
592 QQuickWindow::setGraphicsApi() early on before constructing the first
593 QQuickWidget, or by setting the \c{QSG_RHI_BACKEND} environment variable.
594
595 \note One top-level window can only use one single graphics API for
596 rendering. For example, attempting to place a QQuickWidget using Vulkan and
597 a QOpenGLWidget in the widget hierarchy of the same top-level window,
598 problems will occur and one of the widgets will not be rendering as
599 expected.
600
601 \section1 Scene Graph and Context Persistency
602
603 QQuickWidget honors QQuickWindow::isPersistentSceneGraph(), meaning that
604 applications can decide - by calling
605 QQuickWindow::setPersistentSceneGraph() on the window returned from the
606 quickWindow() function - to let scenegraph nodes and other Qt Quick scene
607 related resources be released whenever the widget becomes hidden. By default
608 persistency is enabled, just like with QQuickWindow.
609
610 When running with the OpenGL, QQuickWindow offers the possibility to
611 disable persistent OpenGL contexts as well. This setting is currently
612 ignored by QQuickWidget and the context is always persistent. The OpenGL
613 context is thus not destroyed when hiding the widget. The context is
614 destroyed only when the widget is destroyed or when the widget gets
615 reparented into another top-level widget's child hierarchy. However, some
616 applications, in particular those that have their own graphics resources
617 due to performing custom OpenGL rendering in the Qt Quick scene, may wish
618 to disable the latter since they may not be prepared to handle the loss of
619 the context when moving a QQuickWidget into another window. Such
620 applications can set the QCoreApplication::AA_ShareOpenGLContexts
621 attribute. For a discussion on the details of resource initialization and
622 cleanup, refer to the QOpenGLWidget documentation.
623
624 \note QQuickWidget offers less fine-grained control over its internal
625 OpenGL context than QOpenGLWidget, and there are subtle differences, most
626 notably that disabling the persistent scene graph will lead to destroying
627 the context on a window change regardless of the presence of
628 QCoreApplication::AA_ShareOpenGLContexts.
629
630 \section1 Limitations
631
632 Putting other widgets underneath and making the QQuickWidget transparent will not lead
633 to the expected results: the widgets underneath will not be visible. This is because
634 in practice the QQuickWidget is drawn before all other regular, non-OpenGL widgets,
635 and so see-through types of solutions are not feasible. Other type of layouts, like
636 having widgets on top of the QQuickWidget, will function as expected.
637
638 When absolutely necessary, this limitation can be overcome by setting the
639 Qt::WA_AlwaysStackOnTop attribute on the QQuickWidget. Be aware, however that this
640 breaks stacking order. For example it will not be possible to have other widgets on
641 top of the QQuickWidget, so it should only be used in situations where a
642 semi-transparent QQuickWidget with other widgets visible underneath is required.
643
644 This limitation only applies when there are other widgets underneath the QQuickWidget
645 inside the same window. Making the window semi-transparent, with other applications
646 and the desktop visible in the background, is done in the traditional way: Set
647 Qt::WA_TranslucentBackground on the top-level window, request an alpha channel, and
648 change the Qt Quick Scenegraph's clear color to Qt::transparent via setClearColor().
649
650 \sa {Exposing Attributes of C++ Types to QML}, {Qt Quick Widgets Example}, QQuickView
651*/
652
653
654/*!
655 \fn void QQuickWidget::statusChanged(QQuickWidget::Status status)
656 This signal is emitted when the component's current \a status changes.
657*/
658
659/*!
660 Constructs a QQuickWidget with a default QML engine as a child of \a parent.
661
662 The default value of \a parent is \c nullptr.
663*/
664QQuickWidget::QQuickWidget(QWidget *parent)
665 : QWidget(*(new QQuickWidgetPrivate), parent, {})
666{
667 d_func()->init();
668}
669
670/*!
671 Constructs a QQuickWidget with a default QML engine and the given QML \a source
672 as a child of \a parent.
673
674 The default value of \a parent is \c nullptr.
675 */
676QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
677 : QQuickWidget(parent)
678{
679 setSource(source);
680}
681
682/*!
683 \since 6.9
684 Constructs a QQuickWidget with the element specified by \a uri and \a typeName
685 and parent \a parent.
686 The default value of \a parent is \c{nullptr}.
687 \sa loadFromModule
688 */
689QQuickWidget::QQuickWidget(QAnyStringView uri, QAnyStringView typeName, QWidget *parent)
690 : QQuickWidget(parent)
691{
692 loadFromModule(uri, typeName);
693}
694
695/*!
696 Constructs a QQuickWidget with the given QML \a engine as a child of \a parent.
697
698 \note The QQuickWidget does not take ownership of the given \a engine object;
699 it is the caller's responsibility to destroy the engine. If the \a engine is deleted
700 before the view, \l status() will return \l QQuickWidget::Error.
701*/
702QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent)
703 : QWidget(*(new QQuickWidgetPrivate), parent, {})
704{
705 d_func()->init(engine);
706}
707
708/*!
709 Destroys the QQuickWidget.
710*/
711QQuickWidget::~QQuickWidget()
712{
713 // Ensure that the component is destroyed before the engine; the engine may
714 // be a child of the QQuickWidgetPrivate, and will be destroyed by its dtor
715 Q_D(QQuickWidget);
716 delete d->root;
717 d->root = nullptr;
718
719 if (d->rhi)
720 d->rhi->removeCleanupCallback(this);
721
722 // NB! resetting graphics resources must be done from this destructor,
723 // *not* from the private class' destructor. This is due to how destruction
724 // works and due to the QWidget dtor (for toplevels) destroying the repaint
725 // manager and rhi before the (QObject) private gets destroyed. Hence must
726 // do it here early on.
727 d->destroy();
728}
729
730/*!
731 \property QQuickWidget::source
732 \brief The URL of the source of the QML component.
733
734 Ensure that the URL provided is full and correct, in particular, use
735 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
736
737 \note Setting a source URL will result in the QML component being
738 instantiated, even if the URL is unchanged from the current value.
739*/
740
741/*!
742 Sets the source to the \a url, loads the QML component and instantiates it.
743
744 Ensure that the URL provided is full and correct, in particular, use
745 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
746
747 Calling this method multiple times with the same URL will result
748 in the QML component being reinstantiated.
749 */
750void QQuickWidget::setSource(const QUrl& url)
751{
752 Q_D(QQuickWidget);
753 d->source = url;
754 d->execute();
755}
756
757/*!
758 \internal
759
760 Sets the source \a url, \a component and content \a item (root of the QML object hierarchy) directly.
761 */
762void QQuickWidget::setContent(const QUrl& url, QQmlComponent *component, QObject* item)
763{
764 Q_D(QQuickWidget);
765 d->source = url;
766 d->component = component;
767
768 if (d->component && d->component->isError()) {
769 const QList<QQmlError> errorList = d->component->errors();
770 for (const QQmlError &error : errorList) {
771 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
772 << error;
773 }
774 emit statusChanged(status());
775 return;
776 }
777
778 d->setRootObject(item);
779 emit statusChanged(status());
780}
781/*!
782 Sets the initial properties \a initialProperties with which the QML
783 component gets initialized after calling \l QQuickWidget::setSource().
784
785 \note You can only use this function to initialize top-level properties.
786 \note This function should always be called before setSource, as it has
787 no effect once the component has become \c Ready.
788
789 \sa QQmlComponent::createWithInitialProperties()
790 \since 6.9
791*/
792void QQuickWidget::setInitialProperties(const QVariantMap &initialProperties)
793{
794 Q_D(QQuickWidget);
795 d->initialProperties = initialProperties;
796}
797
798/*!
799 \since 6.9
800 Loads the QML component identified by \a uri and \a typeName. If the component
801 is backed by a QML file, \l{source} will be set accordingly. For types defined
802 in \c{C++}, \c{source} will be empty.
803
804 If any \l{source} was set before this method was called, it will be cleared.
805
806 Calling this method multiple times with the same \a uri and \a typeName will result
807 in the QML component being reinstantiated.
808
809 \sa setSource, QQmlComponent::loadFromModule, QQmlApplicationEngine::loadFromModule
810 */
811void QQuickWidget::loadFromModule(QAnyStringView uri, QAnyStringView typeName)
812{
813 Q_D(QQuickWidget);
814 d->source = {}; // clear URL
815 d->execute(uri, typeName);
816}
817
818/*!
819 Returns the source URL, if set.
820
821 \sa setSource()
822 */
823QUrl QQuickWidget::source() const
824{
825 Q_D(const QQuickWidget);
826 return d->source;
827}
828
829/*!
830 Returns a pointer to the QQmlEngine used for instantiating
831 QML Components.
832 */
833QQmlEngine* QQuickWidget::engine() const
834{
835 Q_D(const QQuickWidget);
836 d->ensureEngine();
837 return const_cast<QQmlEngine *>(d->engine.data());
838}
839
840/*!
841 This function returns the root of the context hierarchy. Each QML
842 component is instantiated in a QQmlContext. QQmlContext's are
843 essential for passing data to QML components. In QML, contexts are
844 arranged hierarchically and this hierarchy is managed by the
845 QQmlEngine.
846 */
847QQmlContext* QQuickWidget::rootContext() const
848{
849 Q_D(const QQuickWidget);
850 d->ensureEngine();
851 return d->engine.data()->rootContext();
852}
853
854/*!
855 \enum QQuickWidget::Status
856 Specifies the loading status of the QQuickWidget.
857
858 \value Null This QQuickWidget has no source set.
859 \value Ready This QQuickWidget has loaded and created the QML component.
860 \value Loading This QQuickWidget is loading network data.
861 \value Error One or more errors occurred. Call errors() to retrieve a list
862 of errors.
863*/
864
865/*! \enum QQuickWidget::ResizeMode
866
867 This enum specifies how to resize the view.
868
869 \value SizeViewToRootObject The view resizes with the root item in the QML.
870 \value SizeRootObjectToView The view will automatically resize the root item to the size of the view.
871*/
872
873/*!
874 \fn void QQuickWidget::sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message)
875
876 This signal is emitted when an \a error occurred during scene graph initialization.
877
878 Applications should connect to this signal if they wish to handle errors,
879 like OpenGL context creation failures, in a custom way. When no slot is
880 connected to the signal, the behavior will be different: Quick will print
881 the \a message, or show a message box, and terminate the application.
882
883 This signal will be emitted from the GUI thread.
884
885 \sa QQuickWindow::sceneGraphError()
886 */
887
888/*!
889 \property QQuickWidget::status
890 The component's current \l{QQuickWidget::Status} {status}.
891*/
892
893QQuickWidget::Status QQuickWidget::status() const
894{
895 Q_D(const QQuickWidget);
896 if (!d->engine && !d->source.isEmpty())
897 return QQuickWidget::Error;
898
899 if (!d->component)
900 return QQuickWidget::Null;
901
902 if (d->component->status() == QQmlComponent::Ready && !d->root)
903 return QQuickWidget::Error;
904
905 return QQuickWidget::Status(d->component->status());
906}
907
908/*!
909 Return the list of errors that occurred during the last compile or create
910 operation. When the status is not \l Error, an empty list is returned.
911
912 \sa status
913*/
914QList<QQmlError> QQuickWidget::errors() const
915{
916 Q_D(const QQuickWidget);
917 QList<QQmlError> errs;
918
919 if (d->component)
920 errs = d->component->errors();
921
922 if (!d->engine && !d->source.isEmpty()) {
923 QQmlError error;
924 error.setDescription(QLatin1String("QQuickWidget: invalid qml engine."));
925 errs << error;
926 }
927 if (d->component && d->component->status() == QQmlComponent::Ready && !d->root) {
928 QQmlError error;
929 error.setDescription(QLatin1String("QQuickWidget: invalid root object."));
930 errs << error;
931 }
932
933 return errs;
934}
935
936/*!
937 \property QQuickWidget::resizeMode
938 \brief Determines whether the view should resize the window contents.
939
940 If this property is set to SizeViewToRootObject (the default), the view
941 resizes to the size of the root item in the QML.
942
943 If this property is set to SizeRootObjectToView, the view will
944 automatically resize the root item to the size of the view.
945
946 Regardless of this property, the sizeHint of the view
947 is the initial size of the root item. Note though that
948 since QML may load dynamically, that size may change.
949
950 \sa initialSize()
951*/
952
953void QQuickWidget::setResizeMode(ResizeMode mode)
954{
955 Q_D(QQuickWidget);
956 if (d->resizeMode == mode)
957 return;
958
959 if (d->root) {
960 if (d->resizeMode == SizeViewToRootObject) {
961 QQuickItemPrivate *p = QQuickItemPrivate::get(d->root);
962 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
963 }
964 }
965
966 d->resizeMode = mode;
967 if (d->root) {
968 d->initResize();
969 }
970}
971
973{
974 if (root) {
975 if (resizeMode == QQuickWidget::SizeViewToRootObject) {
976 QQuickItemPrivate *p = QQuickItemPrivate::get(root);
977 p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
978 }
979 }
981}
982
984{
985 Q_Q(QQuickWidget);
986 if (!root)
987 return;
988
989 if (resizeMode == QQuickWidget::SizeViewToRootObject) {
990 QSize newSize = QSize(root->width(), root->height());
991 if (newSize.isValid()) {
992 if (newSize != q->size()) {
993 q->resize(newSize);
994 q->updateGeometry();
995 } else if (offscreenWindow->size().isEmpty()) {
996 // QQuickDeliveryAgentPrivate::deliverHoverEvent() ignores events that
997 // occur outside of QQuickRootItem's geometry, so we need it to match root's size.
998 offscreenWindow->resize(newSize);
999 offscreenWindow->contentItem()->setSize(newSize);
1000 }
1001 }
1002 } else if (resizeMode == QQuickWidget::SizeRootObjectToView) {
1003 const bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
1004 const bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
1005
1006 if (needToUpdateWidth && needToUpdateHeight) {
1007 // Make sure that we have realistic sizing behavior by following
1008 // what on-screen windows would do and resize everything, not just
1009 // the root item. We do this because other types may be relying on
1010 // us to behave correctly.
1011 const QSizeF newSize(q->width(), q->height());
1012 offscreenWindow->resize(newSize.toSize());
1013 offscreenWindow->contentItem()->setSize(newSize);
1014 root->setSize(newSize);
1015 } else if (needToUpdateWidth) {
1016 const int newWidth = q->width();
1017 offscreenWindow->setWidth(newWidth);
1018 offscreenWindow->contentItem()->setWidth(newWidth);
1019 root->setWidth(newWidth);
1020 } else if (needToUpdateHeight) {
1021 const int newHeight = q->height();
1022 offscreenWindow->setHeight(newHeight);
1023 offscreenWindow->contentItem()->setHeight(newHeight);
1024 root->setHeight(newHeight);
1025 }
1026 }
1027}
1028
1029/*!
1030 \internal
1031
1032 Update the position of the offscreen window, so it matches the position of the QQuickWidget.
1033 */
1035{
1036 Q_Q(QQuickWidget);
1037 if (offscreenWindow == nullptr)
1038 return;
1039
1040 const QPoint &pos = q->mapToGlobal(QPoint(0, 0));
1041 if (offscreenWindow->position() != pos)
1042 offscreenWindow->setPosition(pos);
1043}
1044
1046{
1047 QSize rootObjectSize(0,0);
1048 int widthCandidate = -1;
1049 int heightCandidate = -1;
1050 if (root) {
1051 widthCandidate = root->width();
1052 heightCandidate = root->height();
1053 }
1054 if (widthCandidate > 0) {
1055 rootObjectSize.setWidth(widthCandidate);
1056 }
1057 if (heightCandidate > 0) {
1058 rootObjectSize.setHeight(heightCandidate);
1059 }
1060 return rootObjectSize;
1061}
1062
1064{
1065 Q_Q(QQuickWidget);
1066
1067 QString translatedMessage;
1068 QString untranslatedMessage;
1069 QQuickWindowPrivate::rhiCreationFailureMessage(QLatin1String("QRhi"), &translatedMessage, &untranslatedMessage);
1070
1071 static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWidget::sceneGraphError);
1072 const bool signalConnected = q->isSignalConnected(errorSignal);
1073 if (signalConnected)
1074 emit q->sceneGraphError(QQuickWindow::ContextNotAvailable, translatedMessage);
1075
1076#if defined(Q_OS_WIN) && QT_CONFIG(messagebox)
1077 if (!signalConnected && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow())
1078 QMessageBox::critical(q, QCoreApplication::applicationName(), translatedMessage);
1079#endif // Q_OS_WIN
1080 if (!signalConnected)
1081 qFatal("%s", qPrintable(untranslatedMessage));
1082}
1083
1084static inline QPlatformBackingStoreRhiConfig::Api graphicsApiToBackingStoreRhiApi(QSGRendererInterface::GraphicsApi api)
1085{
1086 switch (api) {
1087 case QSGRendererInterface::OpenGL:
1088 return QPlatformBackingStoreRhiConfig::OpenGL;
1089 case QSGRendererInterface::Vulkan:
1090 return QPlatformBackingStoreRhiConfig::Vulkan;
1091 case QSGRendererInterface::Direct3D11:
1092 return QPlatformBackingStoreRhiConfig::D3D11;
1093 case QSGRendererInterface::Direct3D12:
1094 return QPlatformBackingStoreRhiConfig::D3D12;
1095 case QSGRendererInterface::Metal:
1096 return QPlatformBackingStoreRhiConfig::Metal;
1097 default:
1098 return QPlatformBackingStoreRhiConfig::Null;
1099 }
1100}
1101
1102// Never called by Software Rendering backend
1104{
1105 Q_Q(QQuickWidget);
1106
1107 // when reparenting, the rhi may suddenly be different
1108 if (rhi) {
1109 QRhi *backingStoreRhi = QWidgetPrivate::rhi();
1110 if (backingStoreRhi && rhi != backingStoreRhi) {
1111 // Can get here not just when reparenting to a new top-level window,
1112 // but also when switching over from the offscreen infrastructure.
1113 // Do the same that the Window[AboutTo]ChangeInternal events would do.
1114 if (rhi == offscreenRenderer.rhi()) {
1117 } else {
1118 rhi = nullptr;
1119 }
1120 }
1121 }
1122
1123 // On hide-show we may invalidate() (when !isPersistentSceneGraph) but our
1124 // context is kept. We may need to initialize() again, though.
1125 const bool onlyNeedsSgInit = rhi && !offscreenWindow->isSceneGraphInitialized();
1126
1127 if (!onlyNeedsSgInit) {
1128 if (rhi)
1129 return;
1130
1131 if (QRhi *backingStoreRhi = QWidgetPrivate::rhi()) {
1132 rhi = backingStoreRhi;
1133 // We don't own the RHI, so make sure we clean up if it goes away
1134 rhi->addCleanupCallback(q, [this](QRhi *rhi) {
1135 if (this->rhi == rhi) {
1137 deviceLost = true;
1138 this->rhi = nullptr;
1139 }
1140 });
1141 }
1142
1143 if (!rhi) {
1144 // The widget (and its parent chain, if any) may not be shown at
1145 // all, yet one may still want to use it for grabs. This is
1146 // ridiculous of course because the rendering infrastructure is
1147 // tied to the top-level widget that initializes upon expose, but
1148 // it has to be supported.
1149 offscreenRenderer.setConfig(rhiConfig());
1150 offscreenRenderer.setFormat(q->format());
1151 // no window passed in, so no swapchain, but we get a functional QRhi which we own
1152 if (offscreenRenderer.create())
1153 rhi = offscreenRenderer.rhi();
1154 }
1155
1156 // Could be that something else already initialized the window with some
1157 // other graphics API for the QRhi, that's not good.
1158 if (rhi && rhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()))) {
1159 qWarning("The top-level window is not using the expected graphics API for composition, "
1160 "'%s' is not compatible with this QQuickWidget",
1161 rhi->backendName());
1162 rhi = nullptr;
1163 }
1164 }
1165
1166 if (rhi) {
1167 if (!offscreenWindow->isSceneGraphInitialized()) {
1168 offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromRhi(rhi));
1169#if QT_CONFIG(vulkan)
1170 if (QWindow *w = q->window()->windowHandle())
1171 offscreenWindow->setVulkanInstance(w->vulkanInstance());
1172 else if (rhi == offscreenRenderer.rhi())
1173 offscreenWindow->setVulkanInstance(QVulkanDefaultInstance::instance());
1174#endif
1175 renderControl->initialize();
1176 }
1177 } else {
1178 qWarning("QQuickWidget: Failed to get a QRhi from the top-level widget's window");
1179 }
1180}
1181
1182void QQuickWidget::createFramebufferObject()
1183{
1184 Q_D(QQuickWidget);
1185
1186 // Could come from Show -> initializeWithRhi -> sceneGraphInitialized in which case the size may
1187 // still be invalid on some platforms. Bail out. A resize will come later on.
1188 if (size().isEmpty())
1189 return;
1190
1191 // Even though this is just an offscreen window we should set the position on it, as it might be
1192 // useful for an item to know the actual position of the scene.
1193 // Note: The position will be update when we get a move event (see: updatePosition()).
1194 const QPoint &globalPos = mapToGlobal(QPoint(0, 0));
1195 d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height());
1196 d->offscreenWindow->contentItem()->setSize(QSizeF(width(), height()));
1197
1198 if (d->useSoftwareRenderer) {
1199 const QSize imageSize = size() * devicePixelRatio();
1200 d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
1201 d->softwareImage.setDevicePixelRatio(devicePixelRatio());
1202 d->forceFullUpdate = true;
1203 return;
1204 }
1205
1206 if (!d->rhi) {
1207 qWarning("QQuickWidget: Attempted to create output texture with no QRhi");
1208 return;
1209 }
1210
1211 int samples = d->requestedSamples;
1212 if (d->rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer))
1213 samples = QSGRhiSupport::chooseSampleCount(samples, d->rhi);
1214 else
1215 samples = 0;
1216
1217 const int minTexSize = d->rhi->resourceLimit(QRhi::TextureSizeMin);
1218 const int maxTexSize = d->rhi->resourceLimit(QRhi::TextureSizeMax);
1219
1220 QSize fboSize = size() * devicePixelRatio();
1221 if (fboSize.width() > maxTexSize || fboSize.height() > maxTexSize) {
1222 qWarning("QQuickWidget: Requested backing texture size is %dx%d, but the maximum texture size for the 3D API implementation is %dx%d",
1223 fboSize.width(), fboSize.height(),
1224 maxTexSize, maxTexSize);
1225 }
1226 fboSize.setWidth(qMin(maxTexSize, qMax(minTexSize, fboSize.width())));
1227 fboSize.setHeight(qMin(maxTexSize, qMax(minTexSize, fboSize.height())));
1228
1229 // Could be a simple hide - show, in which case the previous texture is just fine.
1230 if (!d->outputTexture) {
1231 d->outputTexture = d->rhi->newTexture(QRhiTexture::RGBA8, fboSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
1232 if (!d->outputTexture->create()) {
1233 qWarning("QQuickWidget: failed to create output texture of size %dx%d",
1234 fboSize.width(), fboSize.height());
1235 }
1236 }
1237 if (!d->depthStencil) {
1238 d->depthStencil = d->rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, fboSize, samples);
1239 if (!d->depthStencil->create()) {
1240 qWarning("QQuickWidget: failed to create depth/stencil buffer of size %dx%d and sample count %d",
1241 fboSize.width(), fboSize.height(), samples);
1242 }
1243 }
1244 if (samples > 1 && !d->msaaBuffer) {
1245 d->msaaBuffer = d->rhi->newRenderBuffer(QRhiRenderBuffer::Color, fboSize, samples);
1246 if (!d->msaaBuffer->create()) {
1247 qWarning("QQuickWidget: failed to create multisample renderbuffer of size %dx%d and sample count %d",
1248 fboSize.width(), fboSize.height(), samples);
1249 }
1250 }
1251 if (!d->rt) {
1252 QRhiTextureRenderTargetDescription rtDesc;
1253 QRhiColorAttachment colorAtt;
1254 if (samples <= 1) {
1255 colorAtt.setTexture(d->outputTexture);
1256 } else {
1257 colorAtt.setRenderBuffer(d->msaaBuffer);
1258 colorAtt.setResolveTexture(d->outputTexture);
1259 }
1260 rtDesc.setColorAttachments({ colorAtt });
1261 rtDesc.setDepthStencilBuffer(d->depthStencil);
1262 d->rt = d->rhi->newTextureRenderTarget(rtDesc);
1263 d->rtRp = d->rt->newCompatibleRenderPassDescriptor();
1264 d->rt->setRenderPassDescriptor(d->rtRp);
1265 d->rt->create();
1266 }
1267 if (d->outputTexture->pixelSize() != fboSize) {
1268 d->outputTexture->setPixelSize(fboSize);
1269 if (!d->outputTexture->create()) {
1270 qWarning("QQuickWidget: failed to create resized output texture of size %dx%d",
1271 fboSize.width(), fboSize.height());
1272 }
1273 d->depthStencil->setPixelSize(fboSize);
1274 if (!d->depthStencil->create()) {
1275 qWarning("QQuickWidget: failed to create resized depth/stencil buffer of size %dx%d",
1276 fboSize.width(), fboSize.height());
1277 }
1278 if (d->msaaBuffer) {
1279 d->msaaBuffer->setPixelSize(fboSize);
1280 if (!d->msaaBuffer->create()) {
1281 qWarning("QQuickWidget: failed to create resized multisample renderbuffer of size %dx%d",
1282 fboSize.width(), fboSize.height());
1283 }
1284 }
1285 }
1286
1287 d->offscreenWindow->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(d->rt));
1288
1289 d->renderControl->setSamples(samples);
1290
1291 // Sanity check: The window must not have an underlying platform window.
1292 // Having one would mean create() was called and platforms that only support
1293 // a single native window were in trouble.
1294 Q_ASSERT(!d->offscreenWindow->handle());
1295}
1296
1297void QQuickWidget::destroyFramebufferObject()
1298{
1299 Q_D(QQuickWidget);
1300
1301 if (d->useSoftwareRenderer) {
1302 d->softwareImage = QImage();
1303 return;
1304 }
1305
1306 delete d->rt;
1307 d->rt = nullptr;
1308 delete d->rtRp;
1309 d->rtRp = nullptr;
1310 delete d->depthStencil;
1311 d->depthStencil = nullptr;
1312 delete d->msaaBuffer;
1313 d->msaaBuffer = nullptr;
1314 delete d->outputTexture;
1315 d->outputTexture = nullptr;
1316}
1317
1318QQuickWidget::ResizeMode QQuickWidget::resizeMode() const
1319{
1320 Q_D(const QQuickWidget);
1321 return d->resizeMode;
1322}
1323
1324/*!
1325 \internal
1326 */
1327void QQuickWidget::continueExecute()
1328{
1329 Q_D(QQuickWidget);
1330 disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
1331
1332 if (d->component->isError()) {
1333 const QList<QQmlError> errorList = d->component->errors();
1334 for (const QQmlError &error : errorList) {
1335 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
1336 << error;
1337 }
1338 emit statusChanged(status());
1339 return;
1340 }
1341
1342 std::unique_ptr<QObject> obj(d->initialProperties.empty()
1343 ? d->component->create()
1344 : d->component->createWithInitialProperties(d->initialProperties));
1345
1346 if (d->component->isError()) {
1347 const QList<QQmlError> errorList = d->component->errors();
1348 for (const QQmlError &error : errorList) {
1349 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
1350 << error;
1351 }
1352 emit statusChanged(status());
1353 return;
1354 }
1355
1356 // If we used loadFromModule, we might not have a URL so far.
1357 // Thus, query the component to retrieve the associated URL, if any
1358 if (d->source.isEmpty())
1359 d->source = d->component->url();
1360
1361 d->setRootObject(obj.release());
1362 emit statusChanged(status());
1363}
1364
1365/*!
1366 \internal
1367*/
1369{
1370 Q_Q(QQuickWidget);
1371 if (root == obj)
1372 return true;
1373
1374 if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) {
1375 root = sgItem;
1376 sgItem->setParentItem(offscreenWindow->contentItem());
1377 } else if (qobject_cast<QWindow *>(obj)) {
1378 qWarning() << "QQuickWidget does not support using windows as a root item." << Qt::endl
1379 << Qt::endl
1380 << "If you wish to create your root window from QML, consider using QQmlApplicationEngine instead." << Qt::endl;
1381 } else {
1382 qWarning() << "QQuickWidget only supports loading of root objects that derive from QQuickItem." << Qt::endl
1383 << Qt::endl
1384 << "Ensure your QML code is written for QtQuick 2, and uses a root that is or" << Qt::endl
1385 << "inherits from QtQuick's Item (not a Timer, QtObject, etc)." << Qt::endl;
1386 delete obj;
1387 root = nullptr;
1388 }
1389 if (root) {
1390 initialSize = rootObjectSize();
1391 bool resized = q->testAttribute(Qt::WA_Resized);
1392 if ((resizeMode == QQuickWidget::SizeViewToRootObject || !resized) &&
1393 initialSize != q->size()) {
1394 q->resize(initialSize);
1395 }
1397 return true;
1398 }
1399
1400 return false;
1401}
1402
1404{
1407 return {};
1408
1409 QPlatformBackingStoreRhiConfig config(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()));
1410
1411 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(offscreenWindow);
1412 // This is only here to support some of the env.vars. (such as
1413 // QSG_RHI_DEBUG_LAYER). There is currently no way to set a
1414 // QQuickGraphicsConfiguration for a QQuickWidget, which means things like
1415 // the pipeline cache are just not available. That is something to support
1416 // on the widget/backingstore level since that's where the QRhi is
1417 // controlled in this case.
1418 const bool debugLayerRequested = wd->graphicsConfig.isDebugLayerEnabled();
1419 config.setDebugLayer(debugLayerRequested);
1420 return config;
1421}
1422
1424{
1425 return { outputTexture, nullptr };
1426}
1427
1429{
1430 QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
1431 flags |= QPlatformTextureList::NeedsPremultipliedAlphaBlending;
1432 return flags;
1433}
1434
1435/*!
1436 \internal
1437 Handle item resize and scene updates.
1438 */
1439void QQuickWidget::timerEvent(QTimerEvent* e)
1440{
1441 Q_D(QQuickWidget);
1442 if (!e || e->timerId() == d->resizetimer.timerId()) {
1443 d->updateSize();
1444 d->resizetimer.stop();
1445 } else if (e->timerId() == d->updateTimer.timerId()) {
1446 d->eventPending = false;
1447 d->updateTimer.stop();
1448 if (d->updatePending)
1449 d->renderSceneGraph();
1450 }
1451}
1452
1453/*!
1454 \internal
1455 Preferred size follows the root object geometry.
1456*/
1457QSize QQuickWidget::sizeHint() const
1458{
1459 Q_D(const QQuickWidget);
1460 QSize rootObjectSize = d->rootObjectSize();
1461 if (rootObjectSize.isEmpty()) {
1462 return size();
1463 } else {
1464 return rootObjectSize;
1465 }
1466}
1467
1468/*!
1469 Returns the initial size of the root object.
1470
1471 If \l resizeMode is SizeRootObjectToView, the root object will be
1472 resized to the size of the view. This function returns the size of the
1473 root object before it was resized.
1474*/
1475QSize QQuickWidget::initialSize() const
1476{
1477 Q_D(const QQuickWidget);
1478 return d->initialSize;
1479}
1480
1481/*!
1482 Returns the view's root \l {QQuickItem} {item}. Can be \nullptr
1483 when setSource() has not been called, if it was called with
1484 broken \l[QML]{QtQuick} code or if the root item is otherwise
1485 undefined.
1486 */
1487QQuickItem *QQuickWidget::rootObject() const
1488{
1489 Q_D(const QQuickWidget);
1490 return d->root;
1491}
1492
1493/*!
1494 \internal
1495 This function handles the \l {QResizeEvent} {resize event}
1496 \a e.
1497 */
1498void QQuickWidget::resizeEvent(QResizeEvent *e)
1499{
1500 Q_D(QQuickWidget);
1501 if (d->resizeMode == SizeRootObjectToView)
1502 d->updateSize();
1503
1504 if (e->size().isEmpty()) {
1505 //stop rendering
1506 d->fakeHidden = true;
1507 return;
1508 }
1509
1510 bool needsSync = false;
1511 if (d->fakeHidden) {
1512 //restart rendering
1513 d->fakeHidden = false;
1514 needsSync = true;
1515 }
1516
1517 // Software Renderer
1518 if (d->useSoftwareRenderer) {
1519 needsSync = true;
1520 if (d->softwareImage.size() != size() * devicePixelRatio()) {
1521 createFramebufferObject();
1522 }
1523 } else {
1524 if (d->rhi) {
1525 // Bail out when receiving a resize after scenegraph invalidation. This can happen
1526 // during hide - resize - show sequences and also during application exit.
1527 if (!d->outputTexture && !d->offscreenWindow->isSceneGraphInitialized())
1528 return;
1529 if (!d->outputTexture || d->outputTexture->pixelSize() != size() * devicePixelRatio()) {
1530 needsSync = true;
1531 createFramebufferObject();
1532 }
1533 } else {
1534 // This will result in a scenegraphInitialized() signal which
1535 // is connected to createFramebufferObject().
1536 needsSync = true;
1537 d->initializeWithRhi();
1538 }
1539
1540 if (!d->rhi) {
1541 qWarning("QQuickWidget::resizeEvent() no QRhi");
1542 return;
1543 }
1544 }
1545
1546 d->render(needsSync);
1547}
1548
1549/*! \reimp */
1550bool QQuickWidget::focusNextPrevChild(bool next)
1551{
1552 Q_D(QQuickWidget);
1553
1554 const auto *da = QQuickWindowPrivate::get(d->offscreenWindow)->deliveryAgentPrivate();
1555 Q_ASSERT(da);
1556
1557 auto *currentTarget = da->focusTargetItem();
1558 Q_ASSERT(currentTarget);
1559
1560 auto *nextTarget = QQuickItemPrivate::nextPrevItemInTabFocusChain(currentTarget, next, false);
1561 // If no child to focus, behaves like its base class (QWidget)
1562 if (!nextTarget)
1563 return QWidget::focusNextPrevChild(next);
1564
1565 // Otherwise, simulates focus event for the offscreen window (QQuickWindow)
1566 const Qt::Key k = next ? Qt::Key_Tab : Qt::Key_Backtab;
1567 QKeyEvent event(QEvent::KeyPress, k, Qt::NoModifier);
1568 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, k, Qt::NoModifier);
1569 QCoreApplication::sendEvent(d->offscreenWindow, &event);
1570
1571 QKeyEvent releaseEvent(QEvent::KeyRelease, k, Qt::NoModifier);
1572 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, k, Qt::NoModifier);
1573 QCoreApplication::sendEvent(d->offscreenWindow, &releaseEvent);
1574
1575 return event.isAccepted();
1576}
1577
1578/*! \reimp */
1579void QQuickWidget::keyPressEvent(QKeyEvent *e)
1580{
1581 Q_D(QQuickWidget);
1582 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(),
1583 e->modifiers());
1584
1585 QCoreApplication::sendEvent(d->offscreenWindow, e);
1586}
1587
1588/*! \reimp */
1589void QQuickWidget::keyReleaseEvent(QKeyEvent *e)
1590{
1591 Q_D(QQuickWidget);
1592 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(),
1593 e->modifiers());
1594
1595 QCoreApplication::sendEvent(d->offscreenWindow, e);
1596}
1597
1598/*! \reimp */
1599void QQuickWidget::mouseMoveEvent(QMouseEvent *e)
1600{
1601 Q_D(QQuickWidget);
1602 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, e->position().x(),
1603 e->position().y());
1604
1605 // Put position into the event's position and scenePosition, and globalPosition into the
1606 // event's globalPosition. This way the scenePosition in e is ignored and is replaced by
1607 // position. This is necessary because QQuickWindow thinks of itself as a
1608 // top-level window always.
1609 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1610 e->button(), e->buttons(), e->modifiers(), e->source());
1611 // It's not just the timestamp but also the globalPressPosition, velocity etc.
1612 mappedEvent.setTimestamp(e->timestamp());
1613 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1614 e->setAccepted(mappedEvent.isAccepted());
1615}
1616
1617/*! \reimp */
1618void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
1619{
1620 Q_D(QQuickWidget);
1621 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
1622 e->button(), e->buttons());
1623
1624 // As the second mouse press is suppressed in widget windows we emulate it here for QML.
1625 // See QTBUG-25831
1626 QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(),
1627 e->button(), e->buttons(), e->modifiers(), e->source());
1628 pressEvent.setTimestamp(e->timestamp());
1629 QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent);
1630 e->setAccepted(pressEvent.isAccepted());
1631 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1632 e->button(), e->buttons(), e->modifiers(), e->source());
1633 mappedEvent.setTimestamp(e->timestamp());
1634 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1635}
1636
1637/*! \reimp */
1638void QQuickWidget::showEvent(QShowEvent *)
1639{
1640 Q_D(QQuickWidget);
1641 bool shouldTriggerUpdate = true;
1642
1643 if (!d->useSoftwareRenderer) {
1644 d->initializeWithRhi();
1645
1646 if (d->offscreenWindow->isSceneGraphInitialized()) {
1647 shouldTriggerUpdate = false;
1648 d->render(true);
1649 // render() may have led to a QQuickWindow::update() call (for
1650 // example, having a scene with a QQuickFramebufferObject::Renderer
1651 // calling update() in its render()) which in turn results in
1652 // renderRequested in the rendercontrol, ending up in
1653 // triggerUpdate. In this case just calling update() is not
1654 // acceptable, we need the full renderSceneGraph issued from
1655 // timerEvent().
1656 if (!d->eventPending && d->updatePending) {
1657 d->updatePending = false;
1658 update();
1659 }
1660 }
1661 }
1662
1663 if (shouldTriggerUpdate)
1664 triggerUpdate();
1665
1666 // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance
1667 d->offscreenWindow->setVisible(true);
1668 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
1669 service->setParentWindow(d->offscreenWindow, window()->windowHandle());
1670}
1671
1672/*! \reimp */
1673void QQuickWidget::hideEvent(QHideEvent *)
1674{
1675 Q_D(QQuickWidget);
1676 if (!d->offscreenWindow->isPersistentSceneGraph())
1677 d->invalidateRenderControl();
1678 // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance
1679 d->offscreenWindow->setVisible(false);
1680 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
1681 service->setParentWindow(d->offscreenWindow, d->offscreenWindow);
1682}
1683
1684/*! \reimp */
1685void QQuickWidget::mousePressEvent(QMouseEvent *e)
1686{
1687 Q_D(QQuickWidget);
1688 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, e->button(),
1689 e->buttons());
1690
1691 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1692 e->button(), e->buttons(), e->modifiers(), e->source());
1693 mappedEvent.setTimestamp(e->timestamp());
1694 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1695 e->setAccepted(mappedEvent.isAccepted());
1696}
1697
1698/*! \reimp */
1699void QQuickWidget::mouseReleaseEvent(QMouseEvent *e)
1700{
1701 Q_D(QQuickWidget);
1702 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, e->button(),
1703 e->buttons());
1704
1705 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1706 e->button(), e->buttons(), e->modifiers(), e->source());
1707 mappedEvent.setTimestamp(e->timestamp());
1708 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1709 e->setAccepted(mappedEvent.isAccepted());
1710}
1711
1712#if QT_CONFIG(wheelevent)
1713/*! \reimp */
1714void QQuickWidget::wheelEvent(QWheelEvent *e)
1715{
1716 Q_D(QQuickWidget);
1717 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
1718 e->angleDelta().x(), e->angleDelta().y());
1719
1720 // Wheel events only have local and global positions, no need to map.
1721 QCoreApplication::sendEvent(d->offscreenWindow, e);
1722}
1723#endif
1724
1725/*!
1726 \reimp
1727*/
1728void QQuickWidget::focusInEvent(QFocusEvent * event)
1729{
1730 Q_D(QQuickWidget);
1731
1732 using FocusTarget = QWindowPrivate::FocusTarget;
1733 const Qt::FocusReason reason = event->reason();
1734
1735 switch (reason) {
1736 // if there has been an item focused:
1737 // set the first item focused, when the reason is TabFocusReason
1738 // set the last item focused, when the reason is BacktabFocusReason
1739 case Qt::TabFocusReason:
1740 case Qt::BacktabFocusReason: {
1741 const bool forward = reason == Qt::FocusReason::TabFocusReason;
1742 const FocusTarget target = forward ? FocusTarget::First : FocusTarget::Last;
1743 QQuickWindowPrivate::get(d->offscreenWindow)->setFocusToTarget(target, reason);
1744 } break;
1745 default:
1746 break;
1747 }
1748
1749 d->offscreenWindow->focusInEvent(event);
1750}
1751
1752/*!
1753 \reimp
1754*/
1755void QQuickWidget::focusOutEvent(QFocusEvent * event)
1756{
1757 Q_D(QQuickWidget);
1758 d->offscreenWindow->focusOutEvent(event);
1759}
1760
1761static Qt::WindowState resolveWindowState(Qt::WindowStates states)
1762{
1763 // No more than one of these 3 can be set
1764 if (states & Qt::WindowMinimized)
1765 return Qt::WindowMinimized;
1766 if (states & Qt::WindowMaximized)
1767 return Qt::WindowMaximized;
1768 if (states & Qt::WindowFullScreen)
1769 return Qt::WindowFullScreen;
1770
1771 // No state means "windowed" - we ignore Qt::WindowActive
1772 return Qt::WindowNoState;
1773}
1774
1775static void remapInputMethodQueryEvent(QObject *object, QInputMethodQueryEvent *e)
1776{
1777 auto item = qobject_cast<QQuickItem *>(object);
1778 if (!item)
1779 return;
1780
1781 // Remap all QRectF values.
1782 for (auto query : {Qt::ImCursorRectangle, Qt::ImAnchorRectangle, Qt::ImInputItemClipRectangle}) {
1783 if (e->queries() & query) {
1784 auto value = e->value(query);
1785 if (value.canConvert<QRectF>())
1786 e->setValue(query, item->mapRectToScene(value.toRectF()));
1787 }
1788 }
1789 // Remap all QPointF values.
1790 if (e->queries() & Qt::ImCursorPosition) {
1791 auto value = e->value(Qt::ImCursorPosition);
1792 if (value.canConvert<QPointF>())
1793 e->setValue(Qt::ImCursorPosition, item->mapToScene(value.toPointF()));
1794 }
1795}
1796
1797/*! \reimp */
1798bool QQuickWidget::event(QEvent *e)
1799{
1800 Q_D(QQuickWidget);
1801
1802 switch (e->type()) {
1803
1804 case QEvent::Leave:
1805 case QEvent::TouchBegin:
1806 case QEvent::TouchEnd:
1807 case QEvent::TouchUpdate:
1808 case QEvent::TouchCancel: {
1809 // Touch events only have local and global positions, no need to map.
1810 bool res = QCoreApplication::sendEvent(d->offscreenWindow, e);
1811 if (e->isAccepted() && e->type() == QEvent::TouchBegin) {
1812 // If the TouchBegin got accepted, then make sure all points that have
1813 // an exclusive grabber are also accepted so that the widget code for
1814 // delivering touch events make this widget an implicit grabber of those
1815 // points.
1816 QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e);
1817 auto deliveredPoints = pointerEvent->points();
1818 for (auto &point : deliveredPoints) {
1819 if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty())
1820 point.setAccepted(true);
1821 }
1822 }
1823 return res;
1824 }
1825
1826 case QEvent::FocusAboutToChange:
1827 return QCoreApplication::sendEvent(d->offscreenWindow, e);
1828
1829 case QEvent::InputMethod:
1830 return QCoreApplication::sendEvent(d->offscreenWindow->focusObject(), e);
1831 case QEvent::InputMethodQuery:
1832 {
1833 bool eventResult = QCoreApplication::sendEvent(d->offscreenWindow->focusObject(), e);
1834 // The result in focusObject are based on offscreenWindow. But
1835 // the inputMethodTransform won't get updated because the focus
1836 // is on QQuickWidget. We need to remap the value based on the
1837 // widget.
1838 remapInputMethodQueryEvent(d->offscreenWindow->focusObject(), static_cast<QInputMethodQueryEvent *>(e));
1839 return eventResult;
1840 }
1841
1842 case QEvent::WindowAboutToChangeInternal:
1843 d->handleWindowAboutToChange();
1844 break;
1845
1846 case QEvent::WindowChangeInternal:
1847 d->handleWindowChange();
1848 break;
1849
1850 case QEvent::ScreenChangeInternal:
1851 {
1852 QScreen *newScreen = screen();
1853 if (d->offscreenWindow)
1854 d->offscreenWindow->setScreen(newScreen);
1855 break;
1856 }
1857 case QEvent::DevicePixelRatioChange:
1858 if (d->useSoftwareRenderer || d->outputTexture) {
1859 // This will check the size taking the devicePixelRatio into account
1860 // and recreate if needed.
1861 createFramebufferObject();
1862 d->render(true);
1863 }
1864 if (d->offscreenWindow) {
1865 QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
1866 QGuiApplication::sendEvent(d->offscreenWindow, &dprChangeEvent);
1867 }
1868 break;
1869 case QEvent::Show:
1870 case QEvent::Move:
1871 d->updatePosition();
1872 break;
1873
1874 case QEvent::WindowStateChange:
1875 d->offscreenWindow->setWindowState(resolveWindowState(windowState()));
1876 break;
1877
1878 case QEvent::ShortcutOverride:
1879 return QCoreApplication::sendEvent(d->offscreenWindow, e);
1880
1881 case QEvent::Enter: {
1882 QEnterEvent *enterEvent = static_cast<QEnterEvent *>(e);
1883 QEnterEvent mappedEvent(enterEvent->position(), enterEvent->scenePosition(),
1884 enterEvent->globalPosition());
1885 const bool ret = QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1886 e->setAccepted(mappedEvent.isAccepted());
1887 return ret;
1888 }
1889 default:
1890 break;
1891 }
1892
1893 return QWidget::event(e);
1894}
1895
1896#if QT_CONFIG(quick_draganddrop)
1897
1898/*! \reimp */
1899void QQuickWidget::dragEnterEvent(QDragEnterEvent *e)
1900{
1901 Q_D(QQuickWidget);
1902 // Don't reject drag events for the entire widget when one
1903 // item rejects the drag enter
1904 d->offscreenWindow->event(e);
1905 e->accept();
1906}
1907
1908/*! \reimp */
1909void QQuickWidget::dragMoveEvent(QDragMoveEvent *e)
1910{
1911 Q_D(QQuickWidget);
1912 // Drag/drop events only have local pos, so no need to map,
1913 // but QQuickWindow::event() does not return true
1914 d->offscreenWindow->event(e);
1915}
1916
1917/*! \reimp */
1918void QQuickWidget::dragLeaveEvent(QDragLeaveEvent *e)
1919{
1920 Q_D(QQuickWidget);
1921 d->offscreenWindow->event(e);
1922}
1923
1924/*! \reimp */
1925void QQuickWidget::dropEvent(QDropEvent *e)
1926{
1927 Q_D(QQuickWidget);
1928 d->offscreenWindow->event(e);
1929}
1930
1931#endif // quick_draganddrop
1932
1933// TODO: try to separate the two cases of
1934// 1. render() unconditionally without sync
1935// 2. sync() and then render if necessary
1936void QQuickWidget::triggerUpdate()
1937{
1938 Q_D(QQuickWidget);
1939 d->updatePending = true;
1940 if (!d->eventPending) {
1941 // There's no sense in immediately kicking a render off now, as
1942 // there may be a number of triggerUpdate calls to come from a multitude
1943 // of different sources (network, touch/mouse/keyboard, timers,
1944 // animations, ...), and we want to batch them all into single frames as
1945 // much as possible for the sake of interactivity and responsiveness.
1946 //
1947 // To achieve this, we set a timer and only perform the rendering when
1948 // this is complete.
1949 const int exhaustDelay = 5;
1950 d->updateTimer.start(exhaustDelay, Qt::PreciseTimer, this);
1951 d->eventPending = true;
1952 }
1953}
1954
1955/*!
1956 Sets the surface \a format for the context and offscreen surface used
1957 by this widget.
1958
1959 Call this function when there is a need to request a context for a
1960 given OpenGL version or profile. The sizes for depth, stencil and
1961 alpha buffers are taken care of automatically and there is no need
1962 to request those explicitly.
1963
1964 \sa QWindow::setFormat(), QWindow::format(), format()
1965*/
1966void QQuickWidget::setFormat(const QSurfaceFormat &format)
1967{
1968 Q_D(QQuickWidget);
1969 QSurfaceFormat currentFormat = d->offscreenWindow->format();
1970 QSurfaceFormat newFormat = format;
1971 newFormat.setDepthBufferSize(qMax(newFormat.depthBufferSize(), currentFormat.depthBufferSize()));
1972 newFormat.setStencilBufferSize(qMax(newFormat.stencilBufferSize(), currentFormat.stencilBufferSize()));
1973 newFormat.setAlphaBufferSize(qMax(newFormat.alphaBufferSize(), currentFormat.alphaBufferSize()));
1974
1975 // Do not include the sample count. Requesting a multisampled context is not necessary
1976 // since we render into an FBO, never to an actual surface. What's more, attempting to
1977 // create a pbuffer with a multisampled config crashes certain implementations. Just
1978 // avoid the entire hassle, the result is the same.
1979 d->requestedSamples = newFormat.samples();
1980 newFormat.setSamples(0);
1981
1982 d->offscreenWindow->setFormat(newFormat);
1983}
1984
1985/*!
1986 Returns the actual surface format.
1987
1988 If the widget has not yet been shown, the requested format is returned.
1989
1990 \sa setFormat()
1991*/
1992QSurfaceFormat QQuickWidget::format() const
1993{
1994 Q_D(const QQuickWidget);
1995 return d->offscreenWindow->format();
1996}
1997
1998/*!
1999 Renders a frame and reads it back into an image.
2000
2001 \note This is a potentially expensive operation.
2002 */
2003QImage QQuickWidget::grabFramebuffer() const
2004{
2005 return const_cast<QQuickWidgetPrivate *>(d_func())->grabFramebuffer();
2006}
2007
2008/*!
2009 Sets the clear \a color. By default this is an opaque color.
2010
2011 To get a semi-transparent QQuickWidget, call this function with
2012 \a color set to Qt::transparent, set the Qt::WA_TranslucentBackground
2013 widget attribute on the top-level window, and request an alpha
2014 channel via setFormat().
2015
2016 \sa QQuickWindow::setColor()
2017 */
2018void QQuickWidget::setClearColor(const QColor &color)
2019{
2020 Q_D(QQuickWidget);
2021 d->offscreenWindow->setColor(color);
2022}
2023
2024/*!
2025 \since 5.5
2026
2027 Returns the offscreen QQuickWindow which is used by this widget to drive
2028 the Qt Quick rendering. This is useful if you want to use QQuickWindow
2029 APIs that are not currently exposed by QQuickWidget, for instance
2030 connecting to the QQuickWindow::beforeRendering() signal in order
2031 to draw native OpenGL content below Qt Quick's own rendering.
2032
2033 \warning Use the return value of this function with caution. In
2034 particular, do not ever attempt to show the QQuickWindow, and be
2035 very careful when using other QWindow-only APIs.
2036
2037 \warning The offscreen window may be deleted (and recreated) during
2038 the life time of the QQuickWidget, particularly when the widget is
2039 moved to another QQuickWindow. If you need to know when the window
2040 has been replaced, connect to its destroyed() signal.
2041*/
2042QQuickWindow *QQuickWidget::quickWindow() const
2043{
2044 Q_D(const QQuickWidget);
2045 return d->offscreenWindow;
2046}
2047
2048/*!
2049 \reimp
2050 */
2051void QQuickWidget::paintEvent(QPaintEvent *event)
2052{
2053 Q_D(QQuickWidget);
2054 if (d->useSoftwareRenderer) {
2055 QPainter painter(this);
2056 d->updateRegion = d->updateRegion.united(event->region());
2057 if (d->updateRegion.isNull()) {
2058 //Paint everything
2059 painter.drawImage(rect(), d->softwareImage);
2060 } else {
2061 QTransform transform;
2062 transform.scale(devicePixelRatio(), devicePixelRatio());
2063 //Paint only the updated areas
2064 QRegion targetRegion;
2065 d->updateRegion.swap(targetRegion);
2066 for (auto targetRect : targetRegion) {
2067 auto sourceRect = transform.mapRect(QRectF(targetRect));
2068 painter.drawImage(targetRect, d->softwareImage, sourceRect);
2069 }
2070 }
2071 }
2072}
2073
2074void QQuickWidget::propagateFocusObjectChanged(QObject *focusObject)
2075{
2076 Q_D(QQuickWidget);
2077 if (QApplication::focusObject() != this)
2078 return;
2079 if (QWindow *window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel))
2080 emit window->focusObjectChanged(focusObject);
2081}
2082
2083QT_END_NAMESPACE
2084
2085#include "moc_qquickwidget_p.cpp"
2086
2087#include "moc_qquickwidget.cpp"
void setVisible(bool visible) override
QRhiRenderBuffer * depthStencil
QSize rootObjectSize() const
bool setRootObject(QObject *)
QImage grabFramebuffer() override
void execute(QAnyStringView uri, QAnyStringView typeName)
QQuickRenderControl * renderControl
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeometry) override
void handleContextCreationFailure(const QSurfaceFormat &format)
void ensureEngine() const
void init(QQmlEngine *e=nullptr)
QQmlComponent * component
QRhiRenderBuffer * msaaBuffer
QPlatformTextureList::Flags textureListFlags() override
QRhiTexture * outputTexture
TextureData texture() const override
QPlatformBackingStoreRhiConfig rhiConfig() const override
void render(bool needsSync)
QWindow * renderWindow(QPoint *offset) override
Reimplemented in subclasses to return the real window this render control is rendering into.
static void remapInputMethodQueryEvent(QObject *object, QInputMethodQueryEvent *e)
static QPlatformBackingStoreRhiConfig::Api graphicsApiToBackingStoreRhiApi(QSGRendererInterface::GraphicsApi api)
static Qt::WindowState resolveWindowState(Qt::WindowStates states)