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