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 \sa {Exposing Attributes of C++ Types to QML}, {Qt Quick Widgets Example}, QQuickView
640*/
641
642
643/*!
644 \fn void QQuickWidget::statusChanged(QQuickWidget::Status status)
645 This signal is emitted when the component's current \a status changes.
646*/
647
648/*!
649 Constructs a QQuickWidget with a default QML engine as a child of \a parent.
650
651 The default value of \a parent is \c nullptr.
652*/
653QQuickWidget::QQuickWidget(QWidget *parent)
654 : QWidget(*(new QQuickWidgetPrivate), parent, {})
655{
656 d_func()->init();
657}
658
659/*!
660 Constructs a QQuickWidget with a default QML engine and the given QML \a source
661 as a child of \a parent.
662
663 The default value of \a parent is \c nullptr.
664 */
665QQuickWidget::QQuickWidget(const QUrl &source, QWidget *parent)
666 : QQuickWidget(parent)
667{
668 setSource(source);
669}
670
671/*!
672 \since 6.9
673 Constructs a QQuickWidget with the element specified by \a uri and \a typeName
674 and parent \a parent.
675 The default value of \a parent is \c{nullptr}.
676 \sa loadFromModule
677 */
678QQuickWidget::QQuickWidget(QAnyStringView uri, QAnyStringView typeName, QWidget *parent)
679 : QQuickWidget(parent)
680{
681 loadFromModule(uri, typeName);
682}
683
684/*!
685 Constructs a QQuickWidget with the given QML \a engine as a child of \a parent.
686
687 \note The QQuickWidget does not take ownership of the given \a engine object;
688 it is the caller's responsibility to destroy the engine. If the \a engine is deleted
689 before the view, \l status() will return \l QQuickWidget::Error.
690*/
691QQuickWidget::QQuickWidget(QQmlEngine* engine, QWidget *parent)
692 : QWidget(*(new QQuickWidgetPrivate), parent, {})
693{
694 d_func()->init(engine);
695}
696
697/*!
698 Destroys the QQuickWidget.
699*/
700QQuickWidget::~QQuickWidget()
701{
702 // Ensure that the component is destroyed before the engine; the engine may
703 // be a child of the QQuickWidgetPrivate, and will be destroyed by its dtor
704 Q_D(QQuickWidget);
705 delete d->root;
706 d->root = nullptr;
707
708 if (d->rhi)
709 d->rhi->removeCleanupCallback(this);
710
711 // NB! resetting graphics resources must be done from this destructor,
712 // *not* from the private class' destructor. This is due to how destruction
713 // works and due to the QWidget dtor (for toplevels) destroying the repaint
714 // manager and rhi before the (QObject) private gets destroyed. Hence must
715 // do it here early on.
716 d->destroy();
717}
718
719/*!
720 \property QQuickWidget::source
721 \brief The URL of the source of the QML component.
722
723 Ensure that the URL provided is full and correct, in particular, use
724 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
725
726 \note Setting a source URL will result in the QML component being
727 instantiated, even if the URL is unchanged from the current value.
728*/
729
730/*!
731 Sets the source to the \a url, loads the QML component and instantiates it.
732
733 Ensure that the URL provided is full and correct, in particular, use
734 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
735
736 Calling this method multiple times with the same URL will result
737 in the QML component being reinstantiated.
738 */
739void QQuickWidget::setSource(const QUrl& url)
740{
741 Q_D(QQuickWidget);
742 d->source = url;
743 d->execute();
744}
745
746/*!
747 \internal
748
749 Sets the source \a url, \a component and content \a item (root of the QML object hierarchy) directly.
750 */
751void QQuickWidget::setContent(const QUrl& url, QQmlComponent *component, QObject* item)
752{
753 Q_D(QQuickWidget);
754 d->source = url;
755 d->component = component;
756
757 if (d->component && d->component->isError()) {
758 const QList<QQmlError> errorList = d->component->errors();
759 for (const QQmlError &error : errorList) {
760 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
761 << error;
762 }
763 emit statusChanged(status());
764 return;
765 }
766
767 d->setRootObject(item);
768 emit statusChanged(status());
769}
770/*!
771 Sets the initial properties \a initialProperties with which the QML
772 component gets initialized after calling \l QQuickWidget::setSource().
773
774 \note You can only use this function to initialize top-level properties.
775 \note This function should always be called before setSource, as it has
776 no effect once the component has become \c Ready.
777
778 \sa QQmlComponent::createWithInitialProperties()
779 \since 6.9
780*/
781void QQuickWidget::setInitialProperties(const QVariantMap &initialProperties)
782{
783 Q_D(QQuickWidget);
784 d->initialProperties = initialProperties;
785}
786
787/*!
788 \since 6.9
789 Loads the QML component identified by \a uri and \a typeName. If the component
790 is backed by a QML file, \l{source} will be set accordingly. For types defined
791 in \c{C++}, \c{source} will be empty.
792
793 If any \l{source} was set before this method was called, it will be cleared.
794
795 Calling this method multiple times with the same \a uri and \a typeName will result
796 in the QML component being reinstantiated.
797
798 \sa setSource, QQmlComponent::loadFromModule, QQmlApplicationEngine::loadFromModule
799 */
800void QQuickWidget::loadFromModule(QAnyStringView uri, QAnyStringView typeName)
801{
802 Q_D(QQuickWidget);
803 d->source = {}; // clear URL
804 d->execute(uri, typeName);
805}
806
807/*!
808 Returns the source URL, if set.
809
810 \sa setSource()
811 */
812QUrl QQuickWidget::source() const
813{
814 Q_D(const QQuickWidget);
815 return d->source;
816}
817
818/*!
819 Returns a pointer to the QQmlEngine used for instantiating
820 QML Components.
821 */
822QQmlEngine* QQuickWidget::engine() const
823{
824 Q_D(const QQuickWidget);
825 d->ensureEngine();
826 return const_cast<QQmlEngine *>(d->engine.data());
827}
828
829/*!
830 This function returns the root of the context hierarchy. Each QML
831 component is instantiated in a QQmlContext. QQmlContext's are
832 essential for passing data to QML components. In QML, contexts are
833 arranged hierarchically and this hierarchy is managed by the
834 QQmlEngine.
835 */
836QQmlContext* QQuickWidget::rootContext() const
837{
838 Q_D(const QQuickWidget);
839 d->ensureEngine();
840 return d->engine.data()->rootContext();
841}
842
843/*!
844 \enum QQuickWidget::Status
845 Specifies the loading status of the QQuickWidget.
846
847 \value Null This QQuickWidget has no source set.
848 \value Ready This QQuickWidget has loaded and created the QML component.
849 \value Loading This QQuickWidget is loading network data.
850 \value Error One or more errors occurred. Call errors() to retrieve a list
851 of errors.
852*/
853
854/*! \enum QQuickWidget::ResizeMode
855
856 This enum specifies how to resize the view.
857
858 \value SizeViewToRootObject The view resizes with the root item in the QML.
859 \value SizeRootObjectToView The view will automatically resize the root item to the size of the view.
860*/
861
862/*!
863 \fn void QQuickWidget::sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message)
864
865 This signal is emitted when an \a error occurred during scene graph initialization.
866
867 Applications should connect to this signal if they wish to handle errors,
868 like OpenGL context creation failures, in a custom way. When no slot is
869 connected to the signal, the behavior will be different: Quick will print
870 the \a message, or show a message box, and terminate the application.
871
872 This signal will be emitted from the GUI thread.
873
874 \sa QQuickWindow::sceneGraphError()
875 */
876
877/*!
878 \property QQuickWidget::status
879 The component's current \l{QQuickWidget::Status} {status}.
880*/
881
882QQuickWidget::Status QQuickWidget::status() const
883{
884 Q_D(const QQuickWidget);
885 if (!d->engine && !d->source.isEmpty())
886 return QQuickWidget::Error;
887
888 if (!d->component)
889 return QQuickWidget::Null;
890
891 if (d->component->status() == QQmlComponent::Ready && !d->root)
892 return QQuickWidget::Error;
893
894 return QQuickWidget::Status(d->component->status());
895}
896
897/*!
898 Return the list of errors that occurred during the last compile or create
899 operation. When the status is not \l Error, an empty list is returned.
900
901 \sa status
902*/
903QList<QQmlError> QQuickWidget::errors() const
904{
905 Q_D(const QQuickWidget);
906 QList<QQmlError> errs;
907
908 if (d->component)
909 errs = d->component->errors();
910
911 if (!d->engine && !d->source.isEmpty()) {
912 QQmlError error;
913 error.setDescription(QLatin1String("QQuickWidget: invalid qml engine."));
914 errs << error;
915 }
916 if (d->component && d->component->status() == QQmlComponent::Ready && !d->root) {
917 QQmlError error;
918 error.setDescription(QLatin1String("QQuickWidget: invalid root object."));
919 errs << error;
920 }
921
922 return errs;
923}
924
925/*!
926 \property QQuickWidget::resizeMode
927 \brief Determines whether the view should resize the window contents.
928
929 If this property is set to SizeViewToRootObject (the default), the view
930 resizes to the size of the root item in the QML.
931
932 If this property is set to SizeRootObjectToView, the view will
933 automatically resize the root item to the size of the view.
934
935 Regardless of this property, the sizeHint of the view
936 is the initial size of the root item. Note though that
937 since QML may load dynamically, that size may change.
938
939 \sa initialSize()
940*/
941
942void QQuickWidget::setResizeMode(ResizeMode mode)
943{
944 Q_D(QQuickWidget);
945 if (d->resizeMode == mode)
946 return;
947
948 if (d->root) {
949 if (d->resizeMode == SizeViewToRootObject) {
950 QQuickItemPrivate *p = QQuickItemPrivate::get(d->root);
951 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
952 }
953 }
954
955 d->resizeMode = mode;
956 if (d->root) {
957 d->initResize();
958 }
959}
960
962{
963 if (root) {
964 if (resizeMode == QQuickWidget::SizeViewToRootObject) {
965 QQuickItemPrivate *p = QQuickItemPrivate::get(root);
966 p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
967 }
968 }
970}
971
973{
974 Q_Q(QQuickWidget);
975 if (!root)
976 return;
977
978 if (resizeMode == QQuickWidget::SizeViewToRootObject) {
979 QSize newSize = QSize(root->width(), root->height());
980 if (newSize.isValid()) {
981 if (newSize != q->size()) {
982 q->resize(newSize);
983 q->updateGeometry();
984 } else if (offscreenWindow->size().isEmpty()) {
985 // QQuickDeliveryAgentPrivate::deliverHoverEvent() ignores events that
986 // occur outside of QQuickRootItem's geometry, so we need it to match root's size.
987 offscreenWindow->resize(newSize);
988 offscreenWindow->contentItem()->setSize(newSize);
989 }
990 }
991 } else if (resizeMode == QQuickWidget::SizeRootObjectToView) {
992 const bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
993 const bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
994
995 if (needToUpdateWidth && needToUpdateHeight) {
996 // Make sure that we have realistic sizing behavior by following
997 // what on-screen windows would do and resize everything, not just
998 // the root item. We do this because other types may be relying on
999 // us to behave correctly.
1000 const QSizeF newSize(q->width(), q->height());
1001 offscreenWindow->resize(newSize.toSize());
1002 offscreenWindow->contentItem()->setSize(newSize);
1003 root->setSize(newSize);
1004 } else if (needToUpdateWidth) {
1005 const int newWidth = q->width();
1006 offscreenWindow->setWidth(newWidth);
1007 offscreenWindow->contentItem()->setWidth(newWidth);
1008 root->setWidth(newWidth);
1009 } else if (needToUpdateHeight) {
1010 const int newHeight = q->height();
1011 offscreenWindow->setHeight(newHeight);
1012 offscreenWindow->contentItem()->setHeight(newHeight);
1013 root->setHeight(newHeight);
1014 }
1015 }
1016}
1017
1018/*!
1019 \internal
1020
1021 Update the position of the offscreen window, so it matches the position of the QQuickWidget.
1022 */
1024{
1025 Q_Q(QQuickWidget);
1026 if (offscreenWindow == nullptr)
1027 return;
1028
1029 const QPoint &pos = q->mapToGlobal(QPoint(0, 0));
1030 if (offscreenWindow->position() != pos)
1031 offscreenWindow->setPosition(pos);
1032}
1033
1035{
1036 QSize rootObjectSize(0,0);
1037 int widthCandidate = -1;
1038 int heightCandidate = -1;
1039 if (root) {
1040 widthCandidate = root->width();
1041 heightCandidate = root->height();
1042 }
1043 if (widthCandidate > 0) {
1044 rootObjectSize.setWidth(widthCandidate);
1045 }
1046 if (heightCandidate > 0) {
1047 rootObjectSize.setHeight(heightCandidate);
1048 }
1049 return rootObjectSize;
1050}
1051
1053{
1054 Q_Q(QQuickWidget);
1055
1056 QString translatedMessage;
1057 QString untranslatedMessage;
1058 QQuickWindowPrivate::rhiCreationFailureMessage(QLatin1String("QRhi"), &translatedMessage, &untranslatedMessage);
1059
1060 static const QMetaMethod errorSignal = QMetaMethod::fromSignal(&QQuickWidget::sceneGraphError);
1061 const bool signalConnected = q->isSignalConnected(errorSignal);
1062 if (signalConnected)
1063 emit q->sceneGraphError(QQuickWindow::ContextNotAvailable, translatedMessage);
1064
1065#if defined(Q_OS_WIN) && QT_CONFIG(messagebox)
1066 if (!signalConnected && !QLibraryInfo::isDebugBuild() && !GetConsoleWindow())
1067 QMessageBox::critical(q, QCoreApplication::applicationName(), translatedMessage);
1068#endif // Q_OS_WIN
1069 if (!signalConnected)
1070 qFatal("%s", qPrintable(untranslatedMessage));
1071}
1072
1073static inline QPlatformBackingStoreRhiConfig::Api graphicsApiToBackingStoreRhiApi(QSGRendererInterface::GraphicsApi api)
1074{
1075 switch (api) {
1076 case QSGRendererInterface::OpenGL:
1077 return QPlatformBackingStoreRhiConfig::OpenGL;
1078 case QSGRendererInterface::Vulkan:
1079 return QPlatformBackingStoreRhiConfig::Vulkan;
1080 case QSGRendererInterface::Direct3D11:
1081 return QPlatformBackingStoreRhiConfig::D3D11;
1082 case QSGRendererInterface::Direct3D12:
1083 return QPlatformBackingStoreRhiConfig::D3D12;
1084 case QSGRendererInterface::Metal:
1085 return QPlatformBackingStoreRhiConfig::Metal;
1086 default:
1087 return QPlatformBackingStoreRhiConfig::Null;
1088 }
1089}
1090
1091// Never called by Software Rendering backend
1093{
1094 Q_Q(QQuickWidget);
1095
1096 // when reparenting, the rhi may suddenly be different
1097 if (rhi) {
1098 QRhi *backingStoreRhi = QWidgetPrivate::rhi();
1099 if (backingStoreRhi && rhi != backingStoreRhi)
1100 rhi = nullptr;
1101 }
1102
1103 // On hide-show we may invalidate() (when !isPersistentSceneGraph) but our
1104 // context is kept. We may need to initialize() again, though.
1105 const bool onlyNeedsSgInit = rhi && !offscreenWindow->isSceneGraphInitialized();
1106
1107 if (!onlyNeedsSgInit) {
1108 if (rhi)
1109 return;
1110
1111 if (QRhi *backingStoreRhi = QWidgetPrivate::rhi()) {
1112 rhi = backingStoreRhi;
1113 // We don't own the RHI, so make sure we clean up if it goes away
1114 rhi->addCleanupCallback(q, [this](QRhi *rhi) {
1115 if (this->rhi == rhi) {
1117 deviceLost = true;
1118 this->rhi = nullptr;
1119 }
1120 });
1121 }
1122
1123 if (!rhi) {
1124 // The widget (and its parent chain, if any) may not be shown at
1125 // all, yet one may still want to use it for grabs. This is
1126 // ridiculous of course because the rendering infrastructure is
1127 // tied to the top-level widget that initializes upon expose, but
1128 // it has to be supported.
1129 offscreenRenderer.setConfig(rhiConfig());
1130 offscreenRenderer.setFormat(q->format());
1131 // no window passed in, so no swapchain, but we get a functional QRhi which we own
1132 if (offscreenRenderer.create())
1133 rhi = offscreenRenderer.rhi();
1134 }
1135
1136 // Could be that something else already initialized the window with some
1137 // other graphics API for the QRhi, that's not good.
1138 if (rhi && rhi->backend() != QBackingStoreRhiSupport::apiToRhiBackend(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()))) {
1139 qWarning("The top-level window is not using the expected graphics API for composition, "
1140 "'%s' is not compatible with this QQuickWidget",
1141 rhi->backendName());
1142 rhi = nullptr;
1143 }
1144 }
1145
1146 if (rhi) {
1147 if (!offscreenWindow->isSceneGraphInitialized()) {
1148 offscreenWindow->setGraphicsDevice(QQuickGraphicsDevice::fromRhi(rhi));
1149#if QT_CONFIG(vulkan)
1150 if (QWindow *w = q->window()->windowHandle())
1151 offscreenWindow->setVulkanInstance(w->vulkanInstance());
1152 else if (rhi == offscreenRenderer.rhi())
1153 offscreenWindow->setVulkanInstance(QVulkanDefaultInstance::instance());
1154#endif
1155 renderControl->initialize();
1156 }
1157 } else {
1158 qWarning("QQuickWidget: Failed to get a QRhi from the top-level widget's window");
1159 }
1160}
1161
1162void QQuickWidget::createFramebufferObject()
1163{
1164 Q_D(QQuickWidget);
1165
1166 // Could come from Show -> initializeWithRhi -> sceneGraphInitialized in which case the size may
1167 // still be invalid on some platforms. Bail out. A resize will come later on.
1168 if (size().isEmpty())
1169 return;
1170
1171 // Even though this is just an offscreen window we should set the position on it, as it might be
1172 // useful for an item to know the actual position of the scene.
1173 // Note: The position will be update when we get a move event (see: updatePosition()).
1174 const QPoint &globalPos = mapToGlobal(QPoint(0, 0));
1175 d->offscreenWindow->setGeometry(globalPos.x(), globalPos.y(), width(), height());
1176 d->offscreenWindow->contentItem()->setSize(QSizeF(width(), height()));
1177
1178 if (d->useSoftwareRenderer) {
1179 const QSize imageSize = size() * devicePixelRatio();
1180 d->softwareImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
1181 d->softwareImage.setDevicePixelRatio(devicePixelRatio());
1182 d->forceFullUpdate = true;
1183 return;
1184 }
1185
1186 if (!d->rhi) {
1187 qWarning("QQuickWidget: Attempted to create output texture with no QRhi");
1188 return;
1189 }
1190
1191 int samples = d->requestedSamples;
1192 if (d->rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer))
1193 samples = QSGRhiSupport::chooseSampleCount(samples, d->rhi);
1194 else
1195 samples = 0;
1196
1197 const int minTexSize = d->rhi->resourceLimit(QRhi::TextureSizeMin);
1198 const int maxTexSize = d->rhi->resourceLimit(QRhi::TextureSizeMax);
1199
1200 QSize fboSize = size() * devicePixelRatio();
1201 if (fboSize.width() > maxTexSize || fboSize.height() > maxTexSize) {
1202 qWarning("QQuickWidget: Requested backing texture size is %dx%d, but the maximum texture size for the 3D API implementation is %dx%d",
1203 fboSize.width(), fboSize.height(),
1204 maxTexSize, maxTexSize);
1205 }
1206 fboSize.setWidth(qMin(maxTexSize, qMax(minTexSize, fboSize.width())));
1207 fboSize.setHeight(qMin(maxTexSize, qMax(minTexSize, fboSize.height())));
1208
1209 // Could be a simple hide - show, in which case the previous texture is just fine.
1210 if (!d->outputTexture) {
1211 d->outputTexture = d->rhi->newTexture(QRhiTexture::RGBA8, fboSize, 1, QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
1212 if (!d->outputTexture->create()) {
1213 qWarning("QQuickWidget: failed to create output texture of size %dx%d",
1214 fboSize.width(), fboSize.height());
1215 }
1216 }
1217 if (!d->depthStencil) {
1218 d->depthStencil = d->rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, fboSize, samples);
1219 if (!d->depthStencil->create()) {
1220 qWarning("QQuickWidget: failed to create depth/stencil buffer of size %dx%d and sample count %d",
1221 fboSize.width(), fboSize.height(), samples);
1222 }
1223 }
1224 if (samples > 1 && !d->msaaBuffer) {
1225 d->msaaBuffer = d->rhi->newRenderBuffer(QRhiRenderBuffer::Color, fboSize, samples);
1226 if (!d->msaaBuffer->create()) {
1227 qWarning("QQuickWidget: failed to create multisample renderbuffer of size %dx%d and sample count %d",
1228 fboSize.width(), fboSize.height(), samples);
1229 }
1230 }
1231 if (!d->rt) {
1232 QRhiTextureRenderTargetDescription rtDesc;
1233 QRhiColorAttachment colorAtt;
1234 if (samples <= 1) {
1235 colorAtt.setTexture(d->outputTexture);
1236 } else {
1237 colorAtt.setRenderBuffer(d->msaaBuffer);
1238 colorAtt.setResolveTexture(d->outputTexture);
1239 }
1240 rtDesc.setColorAttachments({ colorAtt });
1241 rtDesc.setDepthStencilBuffer(d->depthStencil);
1242 d->rt = d->rhi->newTextureRenderTarget(rtDesc);
1243 d->rtRp = d->rt->newCompatibleRenderPassDescriptor();
1244 d->rt->setRenderPassDescriptor(d->rtRp);
1245 d->rt->create();
1246 }
1247 if (d->outputTexture->pixelSize() != fboSize) {
1248 d->outputTexture->setPixelSize(fboSize);
1249 if (!d->outputTexture->create()) {
1250 qWarning("QQuickWidget: failed to create resized output texture of size %dx%d",
1251 fboSize.width(), fboSize.height());
1252 }
1253 d->depthStencil->setPixelSize(fboSize);
1254 if (!d->depthStencil->create()) {
1255 qWarning("QQuickWidget: failed to create resized depth/stencil buffer of size %dx%d",
1256 fboSize.width(), fboSize.height());
1257 }
1258 if (d->msaaBuffer) {
1259 d->msaaBuffer->setPixelSize(fboSize);
1260 if (!d->msaaBuffer->create()) {
1261 qWarning("QQuickWidget: failed to create resized multisample renderbuffer of size %dx%d",
1262 fboSize.width(), fboSize.height());
1263 }
1264 }
1265 }
1266
1267 d->offscreenWindow->setRenderTarget(QQuickRenderTarget::fromRhiRenderTarget(d->rt));
1268
1269 d->renderControl->setSamples(samples);
1270
1271 // Sanity check: The window must not have an underlying platform window.
1272 // Having one would mean create() was called and platforms that only support
1273 // a single native window were in trouble.
1274 Q_ASSERT(!d->offscreenWindow->handle());
1275}
1276
1277void QQuickWidget::destroyFramebufferObject()
1278{
1279 Q_D(QQuickWidget);
1280
1281 if (d->useSoftwareRenderer) {
1282 d->softwareImage = QImage();
1283 return;
1284 }
1285
1286 delete d->rt;
1287 d->rt = nullptr;
1288 delete d->rtRp;
1289 d->rtRp = nullptr;
1290 delete d->depthStencil;
1291 d->depthStencil = nullptr;
1292 delete d->msaaBuffer;
1293 d->msaaBuffer = nullptr;
1294 delete d->outputTexture;
1295 d->outputTexture = nullptr;
1296}
1297
1298QQuickWidget::ResizeMode QQuickWidget::resizeMode() const
1299{
1300 Q_D(const QQuickWidget);
1301 return d->resizeMode;
1302}
1303
1304/*!
1305 \internal
1306 */
1307void QQuickWidget::continueExecute()
1308{
1309 Q_D(QQuickWidget);
1310 disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
1311
1312 if (d->component->isError()) {
1313 const QList<QQmlError> errorList = d->component->errors();
1314 for (const QQmlError &error : errorList) {
1315 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
1316 << error;
1317 }
1318 emit statusChanged(status());
1319 return;
1320 }
1321
1322 std::unique_ptr<QObject> obj(d->initialProperties.empty()
1323 ? d->component->create()
1324 : d->component->createWithInitialProperties(d->initialProperties));
1325
1326 if (d->component->isError()) {
1327 const QList<QQmlError> errorList = d->component->errors();
1328 for (const QQmlError &error : errorList) {
1329 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
1330 << error;
1331 }
1332 emit statusChanged(status());
1333 return;
1334 }
1335
1336 // If we used loadFromModule, we might not have a URL so far.
1337 // Thus, query the component to retrieve the associated URL, if any
1338 if (d->source.isEmpty())
1339 d->source = d->component->url();
1340
1341 d->setRootObject(obj.get());
1342 if (d->root)
1343 Q_UNUSED(obj.release());
1344 emit statusChanged(status());
1345}
1346
1347/*!
1348 \internal
1349*/
1351{
1352 Q_Q(QQuickWidget);
1353 if (root == obj)
1354 return true;
1355
1356 if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) {
1357 root = sgItem;
1358 sgItem->setParentItem(offscreenWindow->contentItem());
1359 } else if (qobject_cast<QWindow *>(obj)) {
1360 qWarning() << "QQuickWidget does not support using windows as a root item." << Qt::endl
1361 << Qt::endl
1362 << "If you wish to create your root window from QML, consider using QQmlApplicationEngine instead." << Qt::endl;
1363 } else {
1364 qWarning() << "QQuickWidget only supports loading of root objects that derive from QQuickItem." << Qt::endl
1365 << Qt::endl
1366 << "Ensure your QML code is written for QtQuick 2, and uses a root that is or" << Qt::endl
1367 << "inherits from QtQuick's Item (not a Timer, QtObject, etc)." << Qt::endl;
1368 delete obj;
1369 root = nullptr;
1370 }
1371 if (root) {
1372 initialSize = rootObjectSize();
1373 bool resized = q->testAttribute(Qt::WA_Resized);
1374 if ((resizeMode == QQuickWidget::SizeViewToRootObject || !resized) &&
1375 initialSize != q->size()) {
1376 q->resize(initialSize);
1377 }
1379 return true;
1380 }
1381
1382 return false;
1383}
1384
1386{
1389 return {};
1390
1391 QPlatformBackingStoreRhiConfig config(graphicsApiToBackingStoreRhiApi(QQuickWindow::graphicsApi()));
1392
1393 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(offscreenWindow);
1394 // This is only here to support some of the env.vars. (such as
1395 // QSG_RHI_DEBUG_LAYER). There is currently no way to set a
1396 // QQuickGraphicsConfiguration for a QQuickWidget, which means things like
1397 // the pipeline cache are just not available. That is something to support
1398 // on the widget/backingstore level since that's where the QRhi is
1399 // controlled in this case.
1400 const bool debugLayerRequested = wd->graphicsConfig.isDebugLayerEnabled();
1401 config.setDebugLayer(debugLayerRequested);
1402 return config;
1403}
1404
1406{
1407 return { outputTexture, nullptr };
1408}
1409
1411{
1412 QPlatformTextureList::Flags flags = QWidgetPrivate::textureListFlags();
1413 flags |= QPlatformTextureList::NeedsPremultipliedAlphaBlending;
1414 return flags;
1415}
1416
1417/*!
1418 \internal
1419 Handle item resize and scene updates.
1420 */
1421void QQuickWidget::timerEvent(QTimerEvent* e)
1422{
1423 Q_D(QQuickWidget);
1424 if (!e || e->timerId() == d->resizetimer.timerId()) {
1425 d->updateSize();
1426 d->resizetimer.stop();
1427 } else if (e->timerId() == d->updateTimer.timerId()) {
1428 d->eventPending = false;
1429 d->updateTimer.stop();
1430 if (d->updatePending)
1431 d->renderSceneGraph();
1432 }
1433}
1434
1435/*!
1436 \internal
1437 Preferred size follows the root object geometry.
1438*/
1439QSize QQuickWidget::sizeHint() const
1440{
1441 Q_D(const QQuickWidget);
1442 QSize rootObjectSize = d->rootObjectSize();
1443 if (rootObjectSize.isEmpty()) {
1444 return size();
1445 } else {
1446 return rootObjectSize;
1447 }
1448}
1449
1450/*!
1451 Returns the initial size of the root object.
1452
1453 If \l resizeMode is SizeRootObjectToView, the root object will be
1454 resized to the size of the view. This function returns the size of the
1455 root object before it was resized.
1456*/
1457QSize QQuickWidget::initialSize() const
1458{
1459 Q_D(const QQuickWidget);
1460 return d->initialSize;
1461}
1462
1463/*!
1464 Returns the view's root \l {QQuickItem} {item}. Can be \nullptr
1465 when setSource() has not been called, if it was called with
1466 broken \l[QML]{QtQuick} code or if the root item is otherwise
1467 undefined.
1468 */
1469QQuickItem *QQuickWidget::rootObject() const
1470{
1471 Q_D(const QQuickWidget);
1472 return d->root;
1473}
1474
1475/*!
1476 \internal
1477 This function handles the \l {QResizeEvent} {resize event}
1478 \a e.
1479 */
1480void QQuickWidget::resizeEvent(QResizeEvent *e)
1481{
1482 Q_D(QQuickWidget);
1483 if (d->resizeMode == SizeRootObjectToView)
1484 d->updateSize();
1485
1486 if (e->size().isEmpty()) {
1487 //stop rendering
1488 d->fakeHidden = true;
1489 return;
1490 }
1491
1492 bool needsSync = false;
1493 if (d->fakeHidden) {
1494 //restart rendering
1495 d->fakeHidden = false;
1496 needsSync = true;
1497 }
1498
1499 // Software Renderer
1500 if (d->useSoftwareRenderer) {
1501 needsSync = true;
1502 if (d->softwareImage.size() != size() * devicePixelRatio()) {
1503 createFramebufferObject();
1504 }
1505 } else {
1506 if (d->rhi) {
1507 // Bail out when receiving a resize after scenegraph invalidation. This can happen
1508 // during hide - resize - show sequences and also during application exit.
1509 if (!d->outputTexture && !d->offscreenWindow->isSceneGraphInitialized())
1510 return;
1511 if (!d->outputTexture || d->outputTexture->pixelSize() != size() * devicePixelRatio()) {
1512 needsSync = true;
1513 createFramebufferObject();
1514 }
1515 } else {
1516 // This will result in a scenegraphInitialized() signal which
1517 // is connected to createFramebufferObject().
1518 needsSync = true;
1519 d->initializeWithRhi();
1520 }
1521
1522 if (!d->rhi) {
1523 qWarning("QQuickWidget::resizeEvent() no QRhi");
1524 return;
1525 }
1526 }
1527
1528 d->render(needsSync);
1529}
1530
1531/*! \reimp */
1532bool QQuickWidget::focusNextPrevChild(bool next)
1533{
1534 Q_D(QQuickWidget);
1535
1536 const auto *da = QQuickWindowPrivate::get(d->offscreenWindow)->deliveryAgentPrivate();
1537 Q_ASSERT(da);
1538
1539 auto *currentTarget = da->focusTargetItem();
1540 Q_ASSERT(currentTarget);
1541
1542 auto *nextTarget = QQuickItemPrivate::nextPrevItemInTabFocusChain(currentTarget, next, false);
1543 // If no child to focus, behaves like its base class (QWidget)
1544 if (!nextTarget)
1545 return QWidget::focusNextPrevChild(next);
1546
1547 // Otherwise, simulates focus event for the offscreen window (QQuickWindow)
1548 const Qt::Key k = next ? Qt::Key_Tab : Qt::Key_Backtab;
1549 QKeyEvent event(QEvent::KeyPress, k, Qt::NoModifier);
1550 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, k, Qt::NoModifier);
1551 QCoreApplication::sendEvent(d->offscreenWindow, &event);
1552
1553 QKeyEvent releaseEvent(QEvent::KeyRelease, k, Qt::NoModifier);
1554 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, k, Qt::NoModifier);
1555 QCoreApplication::sendEvent(d->offscreenWindow, &releaseEvent);
1556
1557 return event.isAccepted();
1558}
1559
1560/*! \reimp */
1561void QQuickWidget::keyPressEvent(QKeyEvent *e)
1562{
1563 Q_D(QQuickWidget);
1564 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(),
1565 e->modifiers());
1566
1567 QCoreApplication::sendEvent(d->offscreenWindow, e);
1568}
1569
1570/*! \reimp */
1571void QQuickWidget::keyReleaseEvent(QKeyEvent *e)
1572{
1573 Q_D(QQuickWidget);
1574 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(),
1575 e->modifiers());
1576
1577 QCoreApplication::sendEvent(d->offscreenWindow, e);
1578}
1579
1580/*! \reimp */
1581void QQuickWidget::mouseMoveEvent(QMouseEvent *e)
1582{
1583 Q_D(QQuickWidget);
1584 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, e->position().x(),
1585 e->position().y());
1586
1587 // Put position into the event's position and scenePosition, and globalPosition into the
1588 // event's globalPosition. This way the scenePosition in e is ignored and is replaced by
1589 // position. This is necessary because QQuickWindow thinks of itself as a
1590 // top-level window always.
1591 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1592 e->button(), e->buttons(), e->modifiers(), e->source());
1593 // It's not just the timestamp but also the globalPressPosition, velocity etc.
1594 mappedEvent.setTimestamp(e->timestamp());
1595 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1596 e->setAccepted(mappedEvent.isAccepted());
1597}
1598
1599/*! \reimp */
1600void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e)
1601{
1602 Q_D(QQuickWidget);
1603 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
1604 e->button(), e->buttons());
1605
1606 // As the second mouse press is suppressed in widget windows we emulate it here for QML.
1607 // See QTBUG-25831
1608 QMouseEvent pressEvent(QEvent::MouseButtonPress, e->position(), e->position(), e->globalPosition(),
1609 e->button(), e->buttons(), e->modifiers(), e->source());
1610 pressEvent.setTimestamp(e->timestamp());
1611 QCoreApplication::sendEvent(d->offscreenWindow, &pressEvent);
1612 e->setAccepted(pressEvent.isAccepted());
1613 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1614 e->button(), e->buttons(), e->modifiers(), e->source());
1615 mappedEvent.setTimestamp(e->timestamp());
1616 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1617}
1618
1619/*! \reimp */
1620void QQuickWidget::showEvent(QShowEvent *)
1621{
1622 Q_D(QQuickWidget);
1623 bool shouldTriggerUpdate = true;
1624
1625 if (!d->useSoftwareRenderer) {
1626 d->initializeWithRhi();
1627
1628 if (d->offscreenWindow->isSceneGraphInitialized()) {
1629 shouldTriggerUpdate = false;
1630 d->render(true);
1631 // render() may have led to a QQuickWindow::update() call (for
1632 // example, having a scene with a QQuickFramebufferObject::Renderer
1633 // calling update() in its render()) which in turn results in
1634 // renderRequested in the rendercontrol, ending up in
1635 // triggerUpdate. In this case just calling update() is not
1636 // acceptable, we need the full renderSceneGraph issued from
1637 // timerEvent().
1638 if (!d->eventPending && d->updatePending) {
1639 d->updatePending = false;
1640 update();
1641 }
1642 }
1643 }
1644
1645 if (shouldTriggerUpdate)
1646 triggerUpdate();
1647
1648 // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance
1649 d->offscreenWindow->setVisible(true);
1650 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
1651 service->setParentWindow(d->offscreenWindow, window()->windowHandle());
1652}
1653
1654/*! \reimp */
1655void QQuickWidget::hideEvent(QHideEvent *)
1656{
1657 Q_D(QQuickWidget);
1658 if (!d->offscreenWindow->isPersistentSceneGraph())
1659 d->invalidateRenderControl();
1660 // note offscreenWindow is "QQuickWidgetOffscreenWindow" instance
1661 d->offscreenWindow->setVisible(false);
1662 if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
1663 service->setParentWindow(d->offscreenWindow, d->offscreenWindow);
1664}
1665
1666/*! \reimp */
1667void QQuickWidget::mousePressEvent(QMouseEvent *e)
1668{
1669 Q_D(QQuickWidget);
1670 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, e->button(),
1671 e->buttons());
1672
1673 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1674 e->button(), e->buttons(), e->modifiers(), e->source());
1675 mappedEvent.setTimestamp(e->timestamp());
1676 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1677 e->setAccepted(mappedEvent.isAccepted());
1678}
1679
1680/*! \reimp */
1681void QQuickWidget::mouseReleaseEvent(QMouseEvent *e)
1682{
1683 Q_D(QQuickWidget);
1684 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, e->button(),
1685 e->buttons());
1686
1687 QMouseEvent mappedEvent(e->type(), e->position(), e->position(), e->globalPosition(),
1688 e->button(), e->buttons(), e->modifiers(), e->source());
1689 mappedEvent.setTimestamp(e->timestamp());
1690 QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1691 e->setAccepted(mappedEvent.isAccepted());
1692}
1693
1694#if QT_CONFIG(wheelevent)
1695/*! \reimp */
1696void QQuickWidget::wheelEvent(QWheelEvent *e)
1697{
1698 Q_D(QQuickWidget);
1699 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
1700 e->angleDelta().x(), e->angleDelta().y());
1701
1702 // Wheel events only have local and global positions, no need to map.
1703 QCoreApplication::sendEvent(d->offscreenWindow, e);
1704}
1705#endif
1706
1707/*!
1708 \reimp
1709*/
1710void QQuickWidget::focusInEvent(QFocusEvent * event)
1711{
1712 Q_D(QQuickWidget);
1713
1714 using FocusTarget = QWindowPrivate::FocusTarget;
1715 const Qt::FocusReason reason = event->reason();
1716
1717 switch (reason) {
1718 // if there has been an item focused:
1719 // set the first item focused, when the reason is TabFocusReason
1720 // set the last item focused, when the reason is BacktabFocusReason
1721 case Qt::TabFocusReason:
1722 case Qt::BacktabFocusReason: {
1723 const bool forward = reason == Qt::FocusReason::TabFocusReason;
1724 const FocusTarget target = forward ? FocusTarget::First : FocusTarget::Last;
1725 QQuickWindowPrivate::get(d->offscreenWindow)->setFocusToTarget(target, reason);
1726 } break;
1727 default:
1728 break;
1729 }
1730
1731 d->offscreenWindow->focusInEvent(event);
1732}
1733
1734/*!
1735 \reimp
1736*/
1737void QQuickWidget::focusOutEvent(QFocusEvent * event)
1738{
1739 Q_D(QQuickWidget);
1740 d->offscreenWindow->focusOutEvent(event);
1741}
1742
1743static Qt::WindowState resolveWindowState(Qt::WindowStates states)
1744{
1745 // No more than one of these 3 can be set
1746 if (states & Qt::WindowMinimized)
1747 return Qt::WindowMinimized;
1748 if (states & Qt::WindowMaximized)
1749 return Qt::WindowMaximized;
1750 if (states & Qt::WindowFullScreen)
1751 return Qt::WindowFullScreen;
1752
1753 // No state means "windowed" - we ignore Qt::WindowActive
1754 return Qt::WindowNoState;
1755}
1756
1757static void remapInputMethodQueryEvent(QObject *object, QInputMethodQueryEvent *e)
1758{
1759 auto item = qobject_cast<QQuickItem *>(object);
1760 if (!item)
1761 return;
1762
1763 // Remap all QRectF values.
1764 for (auto query : {Qt::ImCursorRectangle, Qt::ImAnchorRectangle, Qt::ImInputItemClipRectangle}) {
1765 if (e->queries() & query) {
1766 auto value = e->value(query);
1767 if (value.canConvert<QRectF>())
1768 e->setValue(query, item->mapRectToScene(value.toRectF()));
1769 }
1770 }
1771 // Remap all QPointF values.
1772 if (e->queries() & Qt::ImCursorPosition) {
1773 auto value = e->value(Qt::ImCursorPosition);
1774 if (value.canConvert<QPointF>())
1775 e->setValue(Qt::ImCursorPosition, item->mapToScene(value.toPointF()));
1776 }
1777}
1778
1779/*! \reimp */
1780bool QQuickWidget::event(QEvent *e)
1781{
1782 Q_D(QQuickWidget);
1783
1784 switch (e->type()) {
1785
1786 case QEvent::Leave:
1787 case QEvent::TouchBegin:
1788 case QEvent::TouchEnd:
1789 case QEvent::TouchUpdate:
1790 case QEvent::TouchCancel: {
1791 // Touch events only have local and global positions, no need to map.
1792 bool res = QCoreApplication::sendEvent(d->offscreenWindow, e);
1793 if (e->isAccepted() && e->type() == QEvent::TouchBegin) {
1794 // If the TouchBegin got accepted, then make sure all points that have
1795 // an exclusive grabber are also accepted so that the widget code for
1796 // delivering touch events make this widget an implicit grabber of those
1797 // points.
1798 QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e);
1799 auto deliveredPoints = pointerEvent->points();
1800 for (auto &point : deliveredPoints) {
1801 if (pointerEvent->exclusiveGrabber(point) || !pointerEvent->passiveGrabbers(point).isEmpty())
1802 point.setAccepted(true);
1803 }
1804 }
1805 return res;
1806 }
1807
1808 case QEvent::FocusAboutToChange:
1809 return QCoreApplication::sendEvent(d->offscreenWindow, e);
1810
1811 case QEvent::InputMethod:
1812 return QCoreApplication::sendEvent(d->offscreenWindow->focusObject(), e);
1813 case QEvent::InputMethodQuery:
1814 {
1815 bool eventResult = QCoreApplication::sendEvent(d->offscreenWindow->focusObject(), e);
1816 // The result in focusObject are based on offscreenWindow. But
1817 // the inputMethodTransform won't get updated because the focus
1818 // is on QQuickWidget. We need to remap the value based on the
1819 // widget.
1820 remapInputMethodQueryEvent(d->offscreenWindow->focusObject(), static_cast<QInputMethodQueryEvent *>(e));
1821 return eventResult;
1822 }
1823
1824 case QEvent::WindowAboutToChangeInternal:
1825 if (d->rhi)
1826 d->rhi->removeCleanupCallback(this);
1827 d->invalidateRenderControl();
1828 d->deviceLost = true;
1829 d->rhi = nullptr;
1830 break;
1831
1832 case QEvent::WindowChangeInternal:
1833 d->handleWindowChange();
1834 break;
1835
1836 case QEvent::ScreenChangeInternal:
1837 {
1838 QScreen *newScreen = screen();
1839 if (d->offscreenWindow)
1840 d->offscreenWindow->setScreen(newScreen);
1841 break;
1842 }
1843 case QEvent::DevicePixelRatioChange:
1844 if (d->useSoftwareRenderer || d->outputTexture) {
1845 // This will check the size taking the devicePixelRatio into account
1846 // and recreate if needed.
1847 createFramebufferObject();
1848 d->render(true);
1849 }
1850 if (d->offscreenWindow) {
1851 QEvent dprChangeEvent(QEvent::DevicePixelRatioChange);
1852 QGuiApplication::sendEvent(d->offscreenWindow, &dprChangeEvent);
1853 }
1854 break;
1855 case QEvent::Show:
1856 case QEvent::Move:
1857 d->updatePosition();
1858 break;
1859
1860 case QEvent::WindowStateChange:
1861 d->offscreenWindow->setWindowState(resolveWindowState(windowState()));
1862 break;
1863
1864 case QEvent::ShortcutOverride:
1865 return QCoreApplication::sendEvent(d->offscreenWindow, e);
1866
1867 case QEvent::Enter: {
1868 QEnterEvent *enterEvent = static_cast<QEnterEvent *>(e);
1869 QEnterEvent mappedEvent(enterEvent->position(), enterEvent->scenePosition(),
1870 enterEvent->globalPosition());
1871 const bool ret = QCoreApplication::sendEvent(d->offscreenWindow, &mappedEvent);
1872 e->setAccepted(mappedEvent.isAccepted());
1873 return ret;
1874 }
1875 default:
1876 break;
1877 }
1878
1879 return QWidget::event(e);
1880}
1881
1882#if QT_CONFIG(quick_draganddrop)
1883
1884/*! \reimp */
1885void QQuickWidget::dragEnterEvent(QDragEnterEvent *e)
1886{
1887 Q_D(QQuickWidget);
1888 // Don't reject drag events for the entire widget when one
1889 // item rejects the drag enter
1890 d->offscreenWindow->event(e);
1891 e->accept();
1892}
1893
1894/*! \reimp */
1895void QQuickWidget::dragMoveEvent(QDragMoveEvent *e)
1896{
1897 Q_D(QQuickWidget);
1898 // Drag/drop events only have local pos, so no need to map,
1899 // but QQuickWindow::event() does not return true
1900 d->offscreenWindow->event(e);
1901}
1902
1903/*! \reimp */
1904void QQuickWidget::dragLeaveEvent(QDragLeaveEvent *e)
1905{
1906 Q_D(QQuickWidget);
1907 d->offscreenWindow->event(e);
1908}
1909
1910/*! \reimp */
1911void QQuickWidget::dropEvent(QDropEvent *e)
1912{
1913 Q_D(QQuickWidget);
1914 d->offscreenWindow->event(e);
1915}
1916
1917#endif // quick_draganddrop
1918
1919// TODO: try to separate the two cases of
1920// 1. render() unconditionally without sync
1921// 2. sync() and then render if necessary
1922void QQuickWidget::triggerUpdate()
1923{
1924 Q_D(QQuickWidget);
1925 d->updatePending = true;
1926 if (!d->eventPending) {
1927 // There's no sense in immediately kicking a render off now, as
1928 // there may be a number of triggerUpdate calls to come from a multitude
1929 // of different sources (network, touch/mouse/keyboard, timers,
1930 // animations, ...), and we want to batch them all into single frames as
1931 // much as possible for the sake of interactivity and responsiveness.
1932 //
1933 // To achieve this, we set a timer and only perform the rendering when
1934 // this is complete.
1935 const int exhaustDelay = 5;
1936 d->updateTimer.start(exhaustDelay, Qt::PreciseTimer, this);
1937 d->eventPending = true;
1938 }
1939}
1940
1941/*!
1942 Sets the surface \a format for the context and offscreen surface used
1943 by this widget.
1944
1945 Call this function when there is a need to request a context for a
1946 given OpenGL version or profile. The sizes for depth, stencil and
1947 alpha buffers are taken care of automatically and there is no need
1948 to request those explicitly.
1949
1950 \sa QWindow::setFormat(), QWindow::format(), format()
1951*/
1952void QQuickWidget::setFormat(const QSurfaceFormat &format)
1953{
1954 Q_D(QQuickWidget);
1955 QSurfaceFormat currentFormat = d->offscreenWindow->format();
1956 QSurfaceFormat newFormat = format;
1957 newFormat.setDepthBufferSize(qMax(newFormat.depthBufferSize(), currentFormat.depthBufferSize()));
1958 newFormat.setStencilBufferSize(qMax(newFormat.stencilBufferSize(), currentFormat.stencilBufferSize()));
1959 newFormat.setAlphaBufferSize(qMax(newFormat.alphaBufferSize(), currentFormat.alphaBufferSize()));
1960
1961 // Do not include the sample count. Requesting a multisampled context is not necessary
1962 // since we render into an FBO, never to an actual surface. What's more, attempting to
1963 // create a pbuffer with a multisampled config crashes certain implementations. Just
1964 // avoid the entire hassle, the result is the same.
1965 d->requestedSamples = newFormat.samples();
1966 newFormat.setSamples(0);
1967
1968 d->offscreenWindow->setFormat(newFormat);
1969}
1970
1971/*!
1972 Returns the actual surface format.
1973
1974 If the widget has not yet been shown, the requested format is returned.
1975
1976 \sa setFormat()
1977*/
1978QSurfaceFormat QQuickWidget::format() const
1979{
1980 Q_D(const QQuickWidget);
1981 return d->offscreenWindow->format();
1982}
1983
1984/*!
1985 Renders a frame and reads it back into an image.
1986
1987 \note This is a potentially expensive operation.
1988 */
1989QImage QQuickWidget::grabFramebuffer() const
1990{
1991 return const_cast<QQuickWidgetPrivate *>(d_func())->grabFramebuffer();
1992}
1993
1994/*!
1995 Sets the clear \a color. By default this is an opaque color.
1996
1997 To get a semi-transparent QQuickWidget, call this function with
1998 \a color set to Qt::transparent, set the Qt::WA_TranslucentBackground
1999 widget attribute on the top-level window, and request an alpha
2000 channel via setFormat().
2001
2002 \sa QQuickWindow::setColor()
2003 */
2004void QQuickWidget::setClearColor(const QColor &color)
2005{
2006 Q_D(QQuickWidget);
2007 d->offscreenWindow->setColor(color);
2008}
2009
2010/*!
2011 \since 5.5
2012
2013 Returns the offscreen QQuickWindow which is used by this widget to drive
2014 the Qt Quick rendering. This is useful if you want to use QQuickWindow
2015 APIs that are not currently exposed by QQuickWidget, for instance
2016 connecting to the QQuickWindow::beforeRendering() signal in order
2017 to draw native OpenGL content below Qt Quick's own rendering.
2018
2019 \warning Use the return value of this function with caution. In
2020 particular, do not ever attempt to show the QQuickWindow, and be
2021 very careful when using other QWindow-only APIs.
2022
2023 \warning The offscreen window may be deleted (and recreated) during
2024 the life time of the QQuickWidget, particularly when the widget is
2025 moved to another QQuickWindow. If you need to know when the window
2026 has been replaced, connect to its destroyed() signal.
2027*/
2028QQuickWindow *QQuickWidget::quickWindow() const
2029{
2030 Q_D(const QQuickWidget);
2031 return d->offscreenWindow;
2032}
2033
2034/*!
2035 \reimp
2036 */
2037void QQuickWidget::paintEvent(QPaintEvent *event)
2038{
2039 Q_D(QQuickWidget);
2040 if (d->useSoftwareRenderer) {
2041 QPainter painter(this);
2042 d->updateRegion = d->updateRegion.united(event->region());
2043 if (d->updateRegion.isNull()) {
2044 //Paint everything
2045 painter.drawImage(rect(), d->softwareImage);
2046 } else {
2047 QTransform transform;
2048 transform.scale(devicePixelRatio(), devicePixelRatio());
2049 //Paint only the updated areas
2050 QRegion targetRegion;
2051 d->updateRegion.swap(targetRegion);
2052 for (auto targetRect : targetRegion) {
2053 auto sourceRect = transform.mapRect(QRectF(targetRect));
2054 painter.drawImage(targetRect, d->softwareImage, sourceRect);
2055 }
2056 }
2057 }
2058}
2059
2060void QQuickWidget::propagateFocusObjectChanged(QObject *focusObject)
2061{
2062 Q_D(QQuickWidget);
2063 if (QApplication::focusObject() != this)
2064 return;
2065 if (QWindow *window = d->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel))
2066 emit window->focusObjectChanged(focusObject);
2067}
2068
2069QT_END_NAMESPACE
2070
2071#include "moc_qquickwidget_p.cpp"
2072
2073#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)