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