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