Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickview.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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// Qt-Security score:significant reason:default
4
5#include "qquickview.h"
6#include "qquickview_p.h"
7
9#include "qquickitem_p.h"
11
12#include <QtQml/qqmlengine.h>
13#include <QtQml/qqmlcomponent.h>
14#include <private/qqmlengine_p.h>
15#include <private/qv4qobjectwrapper_p.h>
16#include <QtCore/qbasictimer.h>
17
18#include <memory>
19
20QT_BEGIN_NAMESPACE
21
22void QQuickViewPrivate::init(QQmlEngine* e)
23{
24 Q_Q(QQuickView);
25
26 engine = e;
27
28 if (engine.isNull())
29 engine = new QQmlEngine(q);
30
31 QQmlEngine::setContextForObject(contentItem, engine.data()->rootContext());
32
33 if (!engine.data()->incubationController())
34 engine.data()->setIncubationController(q->incubationController());
35
36 {
37 // The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS
38 // wrapper so that the garbage collector can see the policy.
39 QV4::ExecutionEngine *v4 = engine.data()->handle();
40 QV4::QObjectWrapper::ensureWrapper(v4, contentItem);
41 }
42}
43
44QQuickViewPrivate::QQuickViewPrivate()
45 : component(nullptr), resizeMode(QQuickView::SizeViewToRootObject), initialSize(0,0)
46{
47}
48
49QQuickViewPrivate::~QQuickViewPrivate()
50{
51}
52
53QQuickViewPrivate::ExecuteState QQuickViewPrivate::executeHelper()
54{
55 if (!engine) {
56 qWarning() << "QQuickView: invalid qml engine.";
57 return Stop;
58 }
59
60 if (root)
61 delete root;
62 if (component) {
63 delete component;
64 component = nullptr;
65 }
66 return ExecuteState::Continue;
67}
68
69void QQuickViewPrivate::execute()
70{
71 if (executeHelper() == Stop)
72 return;
73 Q_Q(QQuickView);
74 if (!source.isEmpty()) {
75 component = new QQmlComponent(engine.data(), source, q);
76 if (!component->isLoading()) {
77 q->continueExecute();
78 } else {
79 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
80 q, SLOT(continueExecute()));
81 }
82 }
83}
84
85void QQuickViewPrivate::execute(QAnyStringView uri, QAnyStringView typeName)
86{
87 if (executeHelper() == Stop)
88 return;
89 Q_Q(QQuickView);
90
91 component = new QQmlComponent(engine.data(), uri, typeName, q);
92 if (!component->isLoading()) {
93 q->continueExecute();
94 } else {
95 QObject::connect(component, SIGNAL(statusChanged(QQmlComponent::Status)),
96 q, SLOT(continueExecute()));
97 }
98
99}
100
101void QQuickViewPrivate::itemGeometryChanged(QQuickItem *resizeItem, QQuickGeometryChange change,
102 const QRectF &oldGeometry)
103{
104 Q_Q(QQuickView);
105 if (resizeItem == root && resizeMode == QQuickView::SizeViewToRootObject) {
106 // wait for both width and height to be changed
107 resizetimer.start(0,q);
108 }
109 QQuickItemChangeListener::itemGeometryChanged(resizeItem, change, oldGeometry);
110}
111
112/*!
113 \class QQuickView
114 \since 5.0
115 \brief The QQuickView class provides a window for displaying a Qt Quick user interface.
116
117 \inmodule QtQuick
118
119 This is a convenience subclass of QQuickWindow which
120 will automatically load and display a QML scene when given the URL of the main source file. Alternatively,
121 you can instantiate your own objects using QQmlComponent and place them in a manually setup QQuickWindow.
122
123 Typical usage:
124
125 \snippet qquickview-ex.cpp 0
126
127 To receive errors related to loading and executing QML with QQuickView,
128 you can connect to the statusChanged() signal and monitor for QQuickView::Error.
129 The errors are available via QQuickView::errors().
130
131 QQuickView also manages sizing of the view and root object. By default, the \l resizeMode
132 is SizeViewToRootObject, which will load the component and resize it to the
133 size of the view. Alternatively the resizeMode may be set to SizeRootObjectToView which
134 will resize the view to the size of the root object.
135
136 \sa {Exposing Attributes of C++ Types to QML}, QQuickWidget
137*/
138
139
140/*! \fn void QQuickView::statusChanged(QQuickView::Status status)
141 This signal is emitted when the component's current \a status changes.
142*/
143
144/*!
145 Constructs a QQuickView with the given \a parent.
146 The default value of \a parent is 0.
147
148*/
149QQuickView::QQuickView(QWindow *parent)
150: QQuickWindow(*(new QQuickViewPrivate), parent)
151{
152 d_func()->init();
153}
154
155/*!
156 Constructs a QQuickView with the given QML \a source and \a parent.
157 The default value of \a parent is \c{nullptr}.
158
159*/
160QQuickView::QQuickView(const QUrl &source, QWindow *parent)
161 : QQuickView(parent)
162{
163 setSource(source);
164}
165
166/*!
167 \since 6.7
168 Constructs a QQuickView with the element specified by \a uri and \a typeName
169 and parent \a parent.
170 The default value of \a parent is \c{nullptr}.
171 \sa loadFromModule
172 */
173QQuickView::QQuickView(QAnyStringView uri, QAnyStringView typeName, QWindow *parent)
174 : QQuickView(parent)
175{
176 loadFromModule(uri, typeName);
177}
178
179/*!
180 Constructs a QQuickView with the given QML \a engine and \a parent.
181
182 Note: In this case, the QQuickView does not own the given \a engine object;
183 it is the caller's responsibility to destroy the engine. If the \a engine is deleted
184 before the view, status() will return QQuickView::Error.
185
186 \sa Status, status(), errors()
187*/
188QQuickView::QQuickView(QQmlEngine* engine, QWindow *parent)
189 : QQuickWindow(*(new QQuickViewPrivate), parent)
190{
191 Q_ASSERT(engine);
192 d_func()->init(engine);
193}
194
195/*!
196 \internal
197*/
198QQuickView::QQuickView(const QUrl &source, QQuickRenderControl *control)
199 : QQuickWindow(*(new QQuickViewPrivate), control)
200{
201 d_func()->init();
202 setSource(source);
203}
204
205/*!
206 Destroys the QQuickView.
207*/
208QQuickView::~QQuickView()
209{
210 // Ensure that the component is destroyed before the engine; the engine may
211 // be a child of the QQuickViewPrivate, and will be destroyed by its dtor
212 Q_D(QQuickView);
213 delete d->root;
214}
215
216/*!
217 \property QQuickView::source
218 \brief The URL of the source of the QML component.
219
220 Ensure that the URL provided is full and correct, in particular, use
221 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
222
223 Note that setting a source URL will result in the QML component being
224 instantiated, even if the URL is unchanged from the current value.
225*/
226
227/*!
228 Sets the source to the \a url, loads the QML component and instantiates it.
229
230 Ensure that the URL provided is full and correct, in particular, use
231 \l QUrl::fromLocalFile() when loading a file from the local filesystem.
232
233 Calling this method multiple times with the same url will result
234 in the QML component being reinstantiated.
235 */
236void QQuickView::setSource(const QUrl& url)
237{
238 Q_D(QQuickView);
239 d->source = url;
240 d->execute();
241}
242
243/*!
244 \since 6.7
245 Loads the QML component identified by \a uri and \a typeName. If the component
246 is backed by a QML file, \l{source} will be set accordingly. For types defined
247 in \c{C++}, \c{source} will be empty.
248
249 If any \l{source} was set before this method was called, it will be cleared.
250
251 Calling this method multiple times with the same \a uri and \a typeName will result
252 in the QML component being reinstantiated.
253
254 \sa setSource, QQmlComponent::loadFromModule, QQmlApplicationEngine::loadFromModule
255 */
256void QQuickView::loadFromModule(QAnyStringView uri, QAnyStringView typeName)
257{
258 Q_D(QQuickView);
259 d->source = {}; // clear URL
260 d->execute(uri, typeName);
261}
262
263/*!
264 Sets the initial properties \a initialProperties with which the QML
265 component gets initialized after calling \l QQuickView::setSource().
266
267 \snippet qquickview-ex.cpp 1
268
269 \note You can only use this function to initialize top-level properties.
270 \note This function should always be called before setSource, as it has
271 no effect once the component has become \c Ready.
272
273 \sa QQmlComponent::createWithInitialProperties()
274 \since 5.14
275*/
276void QQuickView::setInitialProperties(const QVariantMap &initialProperties)
277{
278 Q_D(QQuickView);
279 d->initialProperties = initialProperties;
280}
281
282/*!
283 \internal
284
285 Set the source \a url, \a component and content \a item (root of the QML object hierarchy) directly.
286 */
287void QQuickView::setContent(const QUrl& url, QQmlComponent *component, QObject* item)
288{
289 Q_D(QQuickView);
290 d->source = url;
291 d->component = component;
292
293 if (d->component && d->component->isError()) {
294 const QList<QQmlError> errorList = d->component->errors();
295 for (const QQmlError &error : errorList) {
296 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
297 << error;
298 }
299 emit statusChanged(status());
300 return;
301 }
302
303 if (!d->setRootObject(item))
304 delete item;
305 emit statusChanged(status());
306}
307
308/*!
309 Returns the source URL, if set.
310
311 \sa setSource()
312 */
313QUrl QQuickView::source() const
314{
315 Q_D(const QQuickView);
316 return d->source;
317}
318
319/*!
320 Returns a pointer to the QQmlEngine used for instantiating
321 QML Components.
322 */
323QQmlEngine* QQuickView::engine() const
324{
325 Q_D(const QQuickView);
326 return d->engine ? const_cast<QQmlEngine *>(d->engine.data()) : nullptr;
327}
328
329/*!
330 This function returns the root of the context hierarchy. Each QML
331 component is instantiated in a QQmlContext. QQmlContext's are
332 essential for passing data to QML components. In QML, contexts are
333 arranged hierarchically and this hierarchy is managed by the
334 QQmlEngine.
335 */
336QQmlContext* QQuickView::rootContext() const
337{
338 Q_D(const QQuickView);
339 return d->engine ? d->engine.data()->rootContext() : nullptr;
340}
341
342/*!
343 \enum QQuickView::Status
344 Specifies the loading status of the QQuickView.
345
346 \value Null This QQuickView has no source set.
347 \value Ready This QQuickView has loaded and created the QML component.
348 \value Loading This QQuickView is loading network data.
349 \value Error One or more errors has occurred. Call errors() to retrieve a list
350 of errors.
351*/
352
353/*! \enum QQuickView::ResizeMode
354
355 This enum specifies how to resize the view.
356
357 \value SizeViewToRootObject The view resizes with the root item in the QML.
358 \value SizeRootObjectToView The view will automatically resize the root item to the size of the view.
359*/
360
361/*!
362 \property QQuickView::status
363 The component's current \l{QQuickView::Status} {status}.
364*/
365
366QQuickView::Status QQuickView::status() const
367{
368 Q_D(const QQuickView);
369 if (!d->engine)
370 return QQuickView::Error;
371
372 if (!d->component)
373 return QQuickView::Null;
374
375 if (d->component->status() == QQmlComponent::Ready && !d->root)
376 return QQuickView::Error;
377
378 return QQuickView::Status(d->component->status());
379}
380
381/*!
382 Return the list of errors that occurred during the last compile or create
383 operation. When the status is not Error, an empty list is returned.
384*/
385QList<QQmlError> QQuickView::errors() const
386{
387 Q_D(const QQuickView);
388 QList<QQmlError> errs;
389
390 if (d->component)
391 errs = d->component->errors();
392
393 if (!d->engine) {
394 QQmlError error;
395 error.setDescription(QLatin1String("QQuickView: invalid qml engine."));
396 errs << error;
397 } else if (d->component && d->component->status() == QQmlComponent::Ready && !d->root) {
398 QQmlError error;
399 error.setDescription(QLatin1String("QQuickView: invalid root object."));
400 errs << error;
401 }
402
403 return errs;
404}
405
406/*!
407 \property QQuickView::resizeMode
408 \brief whether the view should resize the window contents
409
410 If this property is set to SizeViewToRootObject (the default), the view
411 resizes to the size of the root item in the QML.
412
413 If this property is set to SizeRootObjectToView, the view will
414 automatically resize the root item to the size of the view.
415
416 \sa initialSize()
417*/
418
419void QQuickView::setResizeMode(ResizeMode mode)
420{
421 Q_D(QQuickView);
422 if (d->resizeMode == mode)
423 return;
424
425 if (d->root) {
426 if (d->resizeMode == SizeViewToRootObject) {
427 QQuickItemPrivate *p = QQuickItemPrivate::get(d->root);
428 p->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
429 }
430 }
431
432 d->resizeMode = mode;
433 if (d->root) {
434 d->initResize();
435 }
436}
437
438void QQuickViewPrivate::initResize()
439{
440 if (root) {
441 if (resizeMode == QQuickView::SizeViewToRootObject) {
442 QQuickItemPrivate *p = QQuickItemPrivate::get(root);
443 p->addItemChangeListener(this, QQuickItemPrivate::Geometry);
444 }
445 }
446 updateSize();
447}
448
449void QQuickViewPrivate::updateSize()
450{
451 Q_Q(QQuickView);
452 if (!root)
453 return;
454
455 if (resizeMode == QQuickView::SizeViewToRootObject) {
456 QSize newSize = QSize(root->width(), root->height());
457 if (newSize.isValid() && newSize != q->size()) {
458 q->resize(newSize);
459 }
460 } else if (resizeMode == QQuickView::SizeRootObjectToView) {
461 bool needToUpdateWidth = !qFuzzyCompare(q->width(), root->width());
462 bool needToUpdateHeight = !qFuzzyCompare(q->height(), root->height());
463
464 if (needToUpdateWidth && needToUpdateHeight)
465 root->setSize(QSizeF(q->width(), q->height()));
466 else if (needToUpdateWidth)
467 root->setWidth(q->width());
468 else if (needToUpdateHeight)
469 root->setHeight(q->height());
470 }
471}
472
473QSize QQuickViewPrivate::rootObjectSize() const
474{
475 QSize rootObjectSize(0,0);
476 int widthCandidate = -1;
477 int heightCandidate = -1;
478 if (root) {
479 widthCandidate = root->width();
480 heightCandidate = root->height();
481 }
482 if (widthCandidate > 0) {
483 rootObjectSize.setWidth(widthCandidate);
484 }
485 if (heightCandidate > 0) {
486 rootObjectSize.setHeight(heightCandidate);
487 }
488 return rootObjectSize;
489}
490
491QQuickView::ResizeMode QQuickView::resizeMode() const
492{
493 Q_D(const QQuickView);
494 return d->resizeMode;
495}
496
497/*!
498 \internal
499 */
500void QQuickView::continueExecute()
501{
502 Q_D(QQuickView);
503 disconnect(d->component, SIGNAL(statusChanged(QQmlComponent::Status)), this, SLOT(continueExecute()));
504
505 if (d->component->isError()) {
506 const QList<QQmlError> errorList = d->component->errors();
507 for (const QQmlError &error : errorList) {
508 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
509 << error;
510 }
511 emit statusChanged(status());
512 return;
513 }
514
515 std::unique_ptr<QObject> obj(d->initialProperties.empty()
516 ? d->component->create()
517 : d->component->createWithInitialProperties(d->initialProperties));
518
519 if (d->component->isError()) {
520 const QList<QQmlError> errorList = d->component->errors();
521 for (const QQmlError &error : errorList) {
522 QMessageLogger(error.url().toString().toLatin1().constData(), error.line(), nullptr).warning()
523 << error;
524 }
525 emit statusChanged(status());
526 return;
527 }
528
529 // If we used loadFromModule, we might not have a URL so far.
530 // Thus, query the component to retrieve the associated URL, if any
531 if (d->source.isEmpty())
532 d->source = d->component->url();
533
534 if (d->setRootObject(obj.get()))
535 Q_UNUSED(obj.release());
536 emit statusChanged(status());
537}
538
539
540/*!
541 \internal
542
543 Sets \a obj as root object and returns true if that operation succeeds.
544 Otherwise returns \c false. If \c false is returned, the root object is
545 \c nullptr afterwards. You can explicitly set the root object to nullptr,
546 and the return value will be \c true.
547*/
548bool QQuickViewPrivate::setRootObject(QObject *obj)
549{
550 Q_Q(QQuickView);
551 if (root == obj)
552 return true;
553
554 delete root;
555 if (obj == nullptr)
556 return true;
557
558 if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) {
559 root = sgItem;
560 root->setFlag(QQuickItem::ItemIsViewport);
561 sgItem->setParentItem(q->QQuickWindow::contentItem());
562 QQml_setParent_noEvent(sgItem, q->QQuickWindow::contentItem());
563 initialSize = rootObjectSize();
564 if ((resizeMode == QQuickView::SizeViewToRootObject || q->width() <= 1 || q->height() <= 1) &&
565 initialSize != q->size()) {
566 q->resize(initialSize);
567 }
568 initResize();
569 return true;
570 }
571
572 if (qobject_cast<QWindow *>(obj)) {
573 qWarning() << "QQuickView does not support using a window as a root item." << Qt::endl
574 << Qt::endl
575 << "If you wish to create your root window from QML, consider using QQmlApplicationEngine instead." << Qt::endl;
576 return false;
577 }
578
579 qWarning() << "QQuickView only supports loading of root objects that derive from QQuickItem." << Qt::endl
580 << Qt::endl
581 << "Ensure your QML code is written for QtQuick 2, and uses a root that is or" << Qt::endl
582 << "inherits from QtQuick's Item (not a Timer, QtObject, etc)." << Qt::endl;
583 return false;
584}
585
586/*!
587 \internal
588 If the \l {QTimerEvent} {timer event} \a e is this
589 view's resize timer, sceneResized() is emitted.
590 */
591void QQuickView::timerEvent(QTimerEvent* e)
592{
593 Q_D(QQuickView);
594 if (!e || e->timerId() == d->resizetimer.timerId()) {
595 d->updateSize();
596 d->resizetimer.stop();
597 }
598}
599
600/*!
601 \internal
602 Preferred size follows the root object geometry.
603*/
604QSize QQuickView::sizeHint() const
605{
606 Q_D(const QQuickView);
607 QSize rootObjectSize = d->rootObjectSize();
608 if (rootObjectSize.isEmpty()) {
609 return size();
610 } else {
611 return rootObjectSize;
612 }
613}
614
615/*!
616 Returns the initial size of the root object.
617
618 If \l resizeMode is QQuickItem::SizeRootObjectToView the root object will be
619 resized to the size of the view. initialSize contains the size of the
620 root object before it was resized.
621*/
622QSize QQuickView::initialSize() const
623{
624 Q_D(const QQuickView);
625 return d->initialSize;
626}
627
628/*!
629 Returns the view's root \l {QQuickItem} {item}.
630 */
631QQuickItem *QQuickView::rootObject() const
632{
633 Q_D(const QQuickView);
634 return d->root;
635}
636
637/*!
638 \internal
639 This function handles the \l {QResizeEvent} {resize event}
640 \a e.
641 */
642void QQuickView::resizeEvent(QResizeEvent *e)
643{
644 Q_D(QQuickView);
645 if (d->resizeMode == SizeRootObjectToView)
646 d->updateSize();
647
648 QQuickWindow::resizeEvent(e);
649}
650
651/*! \reimp */
652void QQuickView::keyPressEvent(QKeyEvent *e)
653{
654 QQuickWindow::keyPressEvent(e);
655}
656
657/*! \reimp */
658void QQuickView::keyReleaseEvent(QKeyEvent *e)
659{
660 QQuickWindow::keyReleaseEvent(e);
661}
662
663/*! \reimp */
664void QQuickView::mouseMoveEvent(QMouseEvent *e)
665{
666 QQuickWindow::mouseMoveEvent(e);
667}
668
669/*! \reimp */
670void QQuickView::mousePressEvent(QMouseEvent *e)
671{
672 QQuickWindow::mousePressEvent(e);
673}
674
675/*! \reimp */
676void QQuickView::mouseReleaseEvent(QMouseEvent *e)
677{
678 QQuickWindow::mouseReleaseEvent(e);
679}
680
681
682QT_END_NAMESPACE
683
684#include "moc_qquickview.cpp"