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