aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickloader.cpp')
-rw-r--r--src/quick/items/qquickloader.cpp218
1 files changed, 99 insertions, 119 deletions
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index d5a822c26f..15cf20ea7a 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickloader_p_p.h"
@@ -197,9 +161,7 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\section2 Loader Sizing Behavior
- If the source component is not an Item type, Loader does not
- apply any special sizing rules. When used to load visual types,
- Loader applies the following sizing rules:
+ When used to load visual types, Loader applies the following sizing rules:
\list
\li If an explicit size is not specified for the Loader, the Loader
@@ -226,6 +188,8 @@ qreal QQuickLoaderPrivate::getImplicitHeight() const
\li The red rectangle will be 50x50, centered in the root item.
\endtable
+ If the source component is not an Item type, Loader does not apply any
+ special sizing rules.
\section2 Receiving Signals from Loaded Objects
@@ -404,7 +368,7 @@ QUrl QQuickLoader::source() const
return d->source;
}
-void QQuickLoader::setSource(const QUrl &url)
+void QQuickLoader::setSourceWithoutResolve(const QUrl &url)
{
setSource(url, true); // clear previous values
}
@@ -512,6 +476,22 @@ void QQuickLoader::loadFromSourceComponent()
d->load();
}
+
+QUrl QQuickLoader::setSourceUrlHelper(const QUrl &unresolvedUrl)
+{
+ Q_D(QQuickLoader);
+
+ // 1. If setSource is called with a valid url, clear the old component and its corresponding url
+ // 2. If setSource is called with an invalid url(e.g. empty url), clear the old component but
+ // hold the url for old one.(we will compare it with new url later and may update status of loader to Loader.Null)
+ QUrl oldUrl = d->source;
+ d->clear();
+ QUrl sourceUrl = qmlEngine(this)->handle()->callingQmlContext()->resolvedUrl(unresolvedUrl);
+ if (!sourceUrl.isValid())
+ d->source = oldUrl;
+ return sourceUrl;
+}
+
/*!
\qmlmethod object QtQuick::Loader::setSource(url source, object properties)
@@ -576,32 +556,34 @@ void QQuickLoader::loadFromSourceComponent()
\sa source, active
*/
-void QQuickLoader::setSource(QQmlV4Function *args)
+void QQuickLoader::setSource(const QUrl &source, QJSValue properties)
{
- Q_ASSERT(args);
Q_D(QQuickLoader);
- bool ipvError = false;
- args->setReturnValue(QV4::Encode::undefined());
- QV4::Scope scope(args->v4engine());
- QV4::ScopedValue ipv(scope, d->extractInitialPropertyValues(args, this, &ipvError));
- if (ipvError)
+ if (!(properties.isArray() || properties.isObject())) {
+ qmlWarning(this) << QQuickLoader::tr("setSource: value is not an object");
return;
+ }
- // 1. If setSource is called with a valid url, clear the old component and its corresponding url
- // 2. If setSource is called with an invalid url(e.g. empty url), clear the old component but
- // hold the url for old one.(we will compare it with new url later and may update status of loader to Loader.Null)
- QUrl oldUrl = d->source;
- d->clear();
- QUrl sourceUrl = d->resolveSourceUrl(args);
- if (!sourceUrl.isValid())
- d->source = oldUrl;
+ QUrl sourceUrl = setSourceUrlHelper(source);
d->disposeInitialPropertyValues();
- if (!ipv->isUndefined()) {
- d->initialPropertyValues.set(args->v4engine(), ipv);
- }
- d->qmlCallingContext.set(scope.engine, scope.engine->qmlContext());
+ auto engine = qmlEngine(this)->handle();
+ d->initialPropertyValues.set(engine, QJSValuePrivate::takeManagedValue(&properties)->asReturnedValue());
+ d->qmlCallingContext.set(engine, engine->qmlContext());
+
+ setSource(sourceUrl, false); // already cleared and set ipv above.
+}
+
+void QQuickLoader::setSource(const QUrl &source)
+{
+ Q_D(QQuickLoader);
+
+ QUrl sourceUrl = setSourceUrlHelper(source);
+
+ d->disposeInitialPropertyValues();
+ auto engine = qmlEngine(this)->handle();
+ d->qmlCallingContext.set(engine, engine->qmlContext());
setSource(sourceUrl, false); // already cleared and set ipv above.
}
@@ -650,14 +632,15 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj)
// does, then set the item's size now before bindings are
// evaluated, otherwise we will end up resizing the item
// later and triggering any affected bindings/anchors.
- if (widthValid && !QQuickItemPrivate::get(item)->widthValid)
+ if (widthValid() && !QQuickItemPrivate::get(item)->widthValid())
item->setWidth(q->width());
- if (heightValid && !QQuickItemPrivate::get(item)->heightValid)
+ if (heightValid() && !QQuickItemPrivate::get(item)->heightValid())
item->setHeight(q->height());
item->setParentItem(q);
}
if (obj) {
- QQml_setParent_noEvent(itemContext, obj);
+ if (itemContext)
+ QQml_setParent_noEvent(itemContext, obj);
QQml_setParent_noEvent(obj, q);
itemContext = nullptr;
}
@@ -690,13 +673,6 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status)
if (status == QQmlIncubator::Ready) {
object = incubator->object();
item = qmlobject_cast<QQuickItem*>(object);
- if (!item) {
- QQuickWindow *window = qmlobject_cast<QQuickWindow*>(object);
- if (window) {
- qCDebug(lcTransient) << window << "is transient for" << q->window();
- window->setTransientParent(q->window());
- }
- }
emit q->itemChanged();
initResize();
incubator->clear();
@@ -736,15 +712,26 @@ void QQuickLoaderPrivate::_q_sourceLoaded()
return;
}
+ if (!active)
+ return;
+
QQmlContext *creationContext = component->creationContext();
- if (!creationContext) creationContext = qmlContext(q);
- itemContext = new QQmlContext(creationContext);
- itemContext->setContextObject(q);
+ if (!creationContext)
+ creationContext = qmlContext(q);
+
+ QQmlComponentPrivate *cp = QQmlComponentPrivate::get(component);
+ QQmlContext *context = [&](){
+ if (cp->isBound())
+ return creationContext;
+ itemContext = new QQmlContext(creationContext);
+ itemContext->setContextObject(q);
+ return itemContext;
+ }();
delete incubator;
incubator = new QQuickLoaderIncubator(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
- component->create(*incubator, itemContext);
+ component->create(*incubator, context);
if (incubator && incubator->status() == QQmlIncubator::Loading)
updateStatus();
@@ -801,7 +788,7 @@ void QQuickLoader::componentComplete()
{
Q_D(QQuickLoader);
QQuickItem::componentComplete();
- if (active()) {
+ if (active() && (status() != Ready)) {
if (d->loadingFromSource)
d->createComponent();
d->load();
@@ -810,12 +797,15 @@ void QQuickLoader::componentComplete()
void QQuickLoader::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
{
- if (change == ItemSceneChange) {
- QQuickWindow *loadedWindow = qmlobject_cast<QQuickWindow *>(item());
- if (loadedWindow) {
- qCDebug(lcTransient) << loadedWindow << "is transient for" << value.window;
- loadedWindow->setTransientParent(value.window);
- }
+ switch (change) {
+ case ItemChildAddedChange:
+ Q_ASSERT(value.item);
+ if (value.item->flags().testFlag(QQuickItem::ItemObservesViewport))
+ // Re-trigger the parent traversal to get subtreeTransformChangedEnabled turned on
+ value.item->setFlag(QQuickItem::ItemObservesViewport);
+ break;
+ default:
+ break;
}
QQuickItem::itemChange(change, value);
}
@@ -921,12 +911,24 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
const bool needToUpdateWidth = loaderGeometryChanged && q->widthValid();
const bool needToUpdateHeight = loaderGeometryChanged && q->heightValid();
- if (needToUpdateWidth && needToUpdateHeight)
+ if (needToUpdateWidth && needToUpdateHeight) {
+ /* setSize keeps bindings intact (for backwards compatibility reasons),
+ but here we actually want the loader to control the size, so any
+ prexisting bindings ought to be removed
+ */
+ auto *itemPriv = QQuickItemPrivate::get(item);
+ // takeBinding would work without the check, but this is more efficient
+ // for the common case where we don't have a binding
+ if (itemPriv->width.hasBinding())
+ itemPriv->width.takeBinding();
+ if (itemPriv->height.hasBinding())
+ itemPriv->height.takeBinding();
item->setSize(QSizeF(q->width(), q->height()));
- else if (needToUpdateWidth)
+ } else if (needToUpdateWidth) {
item->setWidth(q->width());
- else if (needToUpdateHeight)
+ } else if (needToUpdateHeight) {
item->setHeight(q->height());
+ }
if (updatingSize)
return;
@@ -939,7 +941,7 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
}
/*!
- \qmlproperty object QtQuick::Loader::item
+ \qmlproperty QtObject QtQuick::Loader::item
This property holds the top-level object that is currently loaded.
Since \c {QtQuick 2.0}, Loader can load any object type.
@@ -959,34 +961,6 @@ void QQuickLoader::geometryChange(const QRectF &newGeometry, const QRectF &oldGe
QQuickItem::geometryChange(newGeometry, oldGeometry);
}
-QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV4Function *args)
-{
- QV4::Scope scope(args->v4engine());
- QV4::ScopedValue v(scope, (*args)[0]);
- if (v->isUndefined())
- return QUrl();
- QString arg = v->toQString();
- return arg.isEmpty() ? QUrl() : scope.engine->callingQmlContext()->resolvedUrl(QUrl(arg));
-}
-
-QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error)
-{
- QV4::Scope scope(args->v4engine());
- QV4::ScopedValue valuemap(scope, QV4::Encode::undefined());
- if (args->length() >= 2) {
- QV4::ScopedValue v(scope, (*args)[1]);
- if (!v->isObject() || v->as<QV4::ArrayObject>()) {
- *error = true;
- qmlWarning(loader) << QQuickLoader::tr("setSource: value is not an object");
- } else {
- *error = false;
- valuemap = v;
- }
- }
-
- return valuemap->asReturnedValue();
-}
-
QQuickLoader::Status QQuickLoaderPrivate::computeStatus() const
{
if (!active)
@@ -1038,11 +1012,17 @@ void QQuickLoaderPrivate::createComponent()
const QQmlComponent::CompilationMode mode = asynchronous
? QQmlComponent::Asynchronous
: QQmlComponent::PreferSynchronous;
- QQmlContext *context = qmlContext(q);
- component.setObject(new QQmlComponent(
- context->engine(), context->resolvedUrl(source), mode, q), q);
-}
+ if (QQmlContext *context = qmlContext(q)) {
+ if (QQmlEngine *engine = context->engine()) {
+ component.setObject(new QQmlComponent(
+ engine, context->resolvedUrl(source), mode, q), q);
+ return;
+ }
+ }
-#include <moc_qquickloader_p.cpp>
+ qmlWarning(q) << "createComponent: Cannot find a QML engine.";
+}
QT_END_NAMESPACE
+
+#include <moc_qquickloader_p.cpp>