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

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