diff options
Diffstat (limited to 'src/quick/items/qquickloader.cpp')
-rw-r--r-- | src/quick/items/qquickloader.cpp | 218 |
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> |