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