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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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