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

source code of qtdeclarative/src/quickwidgets/qquickwidget.cpp