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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtdeclarative/src/quick/items/qquickview.cpp