diff options
23 files changed, 355 insertions, 37 deletions
diff --git a/examples/quick/rendercontrol/window_singlethreaded.cpp b/examples/quick/rendercontrol/window_singlethreaded.cpp index e43093e241..45f2635ca4 100644 --- a/examples/quick/rendercontrol/window_singlethreaded.cpp +++ b/examples/quick/rendercontrol/window_singlethreaded.cpp @@ -265,9 +265,10 @@ void WindowSingleThreaded::startQuick(const QString &filename) void WindowSingleThreaded::exposeEvent(QExposeEvent *) { if (isExposed()) { - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); - if (!m_quickInitialized) + if (!m_quickInitialized) { + m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); + } } } diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp index 7e1a31f206..4d9f834492 100644 --- a/src/particles/qquickparticleemitter.cpp +++ b/src/particles/qquickparticleemitter.cpp @@ -343,7 +343,7 @@ void QQuickParticleEmitter::emitWindow(int timeStamp) { if (m_system == 0) return; - if ((!m_enabled || !m_particlesPerSecond)&& !m_pulseLeft && m_burstQueue.isEmpty()){ + if ((!m_enabled || m_particlesPerSecond <= 0)&& !m_pulseLeft && m_burstQueue.isEmpty()){ m_reset_last = true; return; } diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index c0b74c4fc6..04d769e4dc 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -448,8 +448,8 @@ right-hand-side of the property declaration must be a valid alias reference: [default] property alias <name>: <alias reference> \endcode -Unlike an ordinary property, an alias can only refer to a object, or the -property of a object, that is within the scope of the \l{QML Object Types} +Unlike an ordinary property, an alias can only refer to an object, or the +property of an object, that is within the scope of the \l{QML Object Types} {type} within which the alias is declared. It cannot contain arbitrary JavaScript expressions and it cannot refer to objects declared outside of the scope of its type. Also note the \e {alias reference} is not optional, diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index b8bd0f7f1d..fbd2d13e40 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -378,6 +378,13 @@ QQmlComponent::~QQmlComponent() if (d->state.completePending) { qWarning("QQmlComponent: Component destroyed while completion pending"); + + if (isError()) { + qWarning() << "This may have been caused by one of the following errors:"; + foreach (const QQmlError &error, d->state.errors) + qWarning().nospace().noquote() << QLatin1String(" ") << error; + } + d->completeCreate(); } diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index adee4330d3..9ba93a1241 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -296,7 +296,7 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD const QFileInfo testPathInfo(testPath); if (testPathInfo.isFile()) { - if (!testPath.endsWith(QStringLiteral(".qml"))) { + if (!testPath.endsWith(QLatin1String(".qml"))) { qWarning("'%s' does not have the suffix '.qml'.", qPrintable(testPath)); return 1; } diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 12bd95101f..f3513f447a 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -605,6 +605,11 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() void QQuickContext2DFBOTexture::endPainting() { QQuickContext2DTexture::endPainting(); + + // There may not be an FBO due to zero width or height. + if (!m_fbo) + return; + if (m_multisampledFbo) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 3663b379bb..3bfd1e8b9a 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -65,6 +65,8 @@ static const int FlickThreshold = 15; // will ensure the Flickable retains the grab on consecutive flicks. static const int RetainGrabVelocity = 100; +static const int MovementEndingTimerInterval = 100; + static qreal EaseOvershoot(qreal t) { return qAtan(t); } @@ -244,6 +246,8 @@ void QQuickFlickablePrivate::init() contentItem->setParentItem(q); qmlobject_connect(&timeline, QQuickTimeLine, SIGNAL(completed()), q, QQuickFlickable, SLOT(timelineCompleted())) + qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()), + q, QQuickFlickable, SLOT(velocityTimelineCompleted())) q->setAcceptedMouseButtons(Qt::LeftButton); q->setFiltersChildMouseEvents(true); QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem); @@ -1202,6 +1206,8 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp hData.velocity = 0; } + if (momentum && !hData.flicking && !vData.flicking) + flickingStarted(hData.velocity != 0, vData.velocity != 0); draggingStarting(); if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) @@ -1391,9 +1397,15 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->maybeBeginDrag(currentTimestamp, event->posF()); break; case Qt::ScrollUpdate: + if (d->scrollingPhase) { + d->pressed = true; + d->movementEndingTimer.start(MovementEndingTimerInterval, this); + } break; case Qt::ScrollEnd: + d->pressed = false; d->scrollingPhase = false; + d->movementEndingTimer.start(MovementEndingTimerInterval, this); d->draggingEnding(); event->accept(); returnToBounds(); @@ -1415,7 +1427,6 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) valid = true; } if (valid) { - d->vData.flicking = false; d->flickY(d->vData.velocity); d->flickingStarted(false, true); if (d->vData.flicking) { @@ -1435,7 +1446,6 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) valid = true; } if (valid) { - d->hData.flicking = false; d->flickX(d->hData.velocity); d->flickingStarted(true, false); if (d->hData.flicking) { @@ -1548,6 +1558,12 @@ void QQuickFlickable::timerEvent(QTimerEvent *event) if (d->delayedPressEvent) { d->replayDelayedPress(); } + } else if (event->timerId() == d->movementEndingTimer.timerId()) { + d->movementEndingTimer.stop(); + d->pressed = false; + d->stealMouse = false; + if (!d->velocityTimeline.isActive()) + movementEnding(true, true); } } @@ -2475,6 +2491,22 @@ bool QQuickFlickable::isMovingVertically() const return d->vData.moving; } +void QQuickFlickable::velocityTimelineCompleted() +{ + Q_D(QQuickFlickable); + if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive()) + || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) { + return; + } + // With subclasses such as GridView, velocityTimeline.completed is emitted repeatedly: + // for example setting currentIndex results in a visual "flick" which the user + // didn't initiate directly. We don't want to end movement repeatedly, and in + // that case movementEnding will happen after the sequence of movements ends. + if (d->vData.flicking) + movementEnding(); + d->updateBeginningEnd(); +} + void QQuickFlickable::timelineCompleted() { Q_D(QQuickFlickable); diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index a7995c5025..6cf78dcf63 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -259,6 +259,7 @@ protected Q_SLOTS: void movementStarting(); void movementEnding(); void movementEnding(bool hMovementEnding, bool vMovementEnding); + void velocityTimelineCompleted(); void timelineCompleted(); protected: diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 83e38af8a9..3c59b19ec2 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -218,6 +218,7 @@ public: bool calcVelocity : 1; bool pixelAligned : 1; QElapsedTimer timer; + QBasicTimer movementEndingTimer; qint64 lastPosTime; qint64 lastPressTime; QPointF lastPos; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 5be1c86fb4..8715fa2772 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7256,7 +7256,7 @@ void QQuickItem::setKeepTouchGrab(bool keep) } /*! - \qmlmethod object QtQuick::Item::contains(point point) + \qmlmethod bool QtQuick::Item::contains(point point) Returns true if this item contains \a point, which is in local coordinates; returns false otherwise. diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 7009a8e328..188b347a20 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -2484,8 +2484,10 @@ void QQuickItemViewPrivate::updateUnrequestedIndexes() void QQuickItemViewPrivate::updateUnrequestedPositions() { - for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) - repositionPackageItemAt(it.key(), it.value()); + for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) { + if (it.value() >= 0) + repositionPackageItemAt(it.key(), it.value()); + } } void QQuickItemViewPrivate::updateVisibleIndex() diff --git a/src/quick/items/qquickwindowattached.cpp b/src/quick/items/qquickwindowattached.cpp index e1cd3c82f2..992bbae382 100644 --- a/src/quick/items/qquickwindowattached.cpp +++ b/src/quick/items/qquickwindowattached.cpp @@ -95,21 +95,21 @@ void QQuickWindowAttached::windowChanged(QQuickWindow *window) if (oldWindow) oldWindow->disconnect(this); - if (!window) - return; // No values to get, therefore nothing to emit - - if (!oldWindow || window->visibility() != oldWindow->visibility()) + if (!oldWindow || !window || window->visibility() != oldWindow->visibility()) emit visibilityChanged(); - if (!oldWindow || window->isActive() != oldWindow->isActive()) + if (!oldWindow || !window || window->isActive() != oldWindow->isActive()) emit activeChanged(); - if (!oldWindow || window->activeFocusItem() != oldWindow->activeFocusItem()) + if (!oldWindow || !window || window->activeFocusItem() != oldWindow->activeFocusItem()) emit activeFocusItemChanged(); emit contentItemChanged(); - if (!oldWindow || window->width() != oldWindow->width()) + if (!oldWindow || !window || window->width() != oldWindow->width()) emit widthChanged(); - if (!oldWindow || window->height() != oldWindow->height()) + if (!oldWindow || !window || window->height() != oldWindow->height()) emit heightChanged(); + if (!window) + return; + // QQuickWindowQmlImpl::visibilityChanged also exists, and window might even // be QQuickWindowQmlImpl, but that's not what we are connecting to. // So this is actual window state rather than a buffered or as-requested one. diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp index f0d4caf7ab..abae6321b0 100644 --- a/src/quick/util/qquickanimator.cpp +++ b/src/quick/util/qquickanimator.cpp @@ -217,7 +217,8 @@ qreal QQuickAnimator::from() const void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, - QQmlProperties &modified) + QQmlProperties &modified, + QObject *defaultTarget) { if (actions.size()) { @@ -249,14 +250,20 @@ void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, // the item when a transition is cancelled. action.fromValue = action.toValue; } - } else { + } + + if (modified.isEmpty()) { job->setTarget(target); job->setFrom(from); job->setTo(to); } - if (!job->target() && defaultProperty.object()) - job->setTarget(qobject_cast<QQuickItem *>(defaultProperty.object())); + if (!job->target()) { + if (defaultProperty.object()) + job->setTarget(qobject_cast<QQuickItem *>(defaultProperty.object())); + else + job->setTarget(qobject_cast<QQuickItem *>(defaultTarget)); + } job->setDuration(duration); job->setLoopCount(loopCount); @@ -266,7 +273,7 @@ void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, QAbstractAnimationJob *QQuickAnimator::transition(QQuickStateActions &actions, QQmlProperties &modified, TransitionDirection direction, - QObject *) + QObject *defaultTarget) { Q_D(QQuickAnimator); @@ -283,7 +290,7 @@ QAbstractAnimationJob *QQuickAnimator::transition(QQuickStateActions &actions, if (!job) return 0; - d->apply(job, propertyName(), actions, modified); + d->apply(job, propertyName(), actions, modified, defaultTarget); if (!job->target()) { delete job; diff --git a/src/quick/util/qquickanimator_p_p.h b/src/quick/util/qquickanimator_p_p.h index 7a61462762..b176119c70 100644 --- a/src/quick/util/qquickanimator_p_p.h +++ b/src/quick/util/qquickanimator_p_p.h @@ -82,7 +82,7 @@ public: uint isFromDefined : 1; uint isToDefined : 1; - void apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, QQmlProperties &modified); + void apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, QQmlProperties &modified, QObject *defaultTarget); }; class QQuickRotationAnimatorPrivate : public QQuickAnimatorPrivate diff --git a/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml b/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml new file mode 100644 index 0000000000..bfd475266e --- /dev/null +++ b/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 + +Column { + width: 200 + height: 200 + + property alias repeater: repeater + property alias transition: transition + + anchors.centerIn: parent + populate: Transition { + id: transition + ScaleAnimator { + from: 0 + to: 1 + } + } + + Repeater { + id: repeater + model: ["red", "green", "blue"] + + Rectangle { + width: 100 + height: 100 + color: modelData + scale: 0 + } + } +} diff --git a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp index 6943bf5045..29142dee12 100644 --- a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp +++ b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp @@ -30,6 +30,8 @@ #include <QtQuick> #include <private/qquickanimator_p.h> +#include <private/qquickrepeater_p.h> +#include <private/qquicktransition_p.h> #include <QtQml> @@ -40,6 +42,7 @@ class tst_Animators: public QObject private slots: void testMultiWinAnimator_data(); void testMultiWinAnimator(); + void testTransitions(); }; void tst_Animators::testMultiWinAnimator_data() @@ -94,6 +97,28 @@ void tst_Animators::testMultiWinAnimator() QVERIFY(true); } +void tst_Animators::testTransitions() +{ + QQuickView view(QUrl::fromLocalFile("data/positionerWithAnimator.qml")); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(view.rootObject()); + + QQuickRepeater *repeater = view.rootObject()->property("repeater").value<QQuickRepeater *>(); + QVERIFY(repeater); + + QQuickItem *child = repeater->itemAt(0); + QVERIFY(child); + QCOMPARE(child->scale(), qreal(0.0)); + + QQuickTransition *transition = view.rootObject()->property("transition").value<QQuickTransition *>(); + QVERIFY(transition); + + QTRY_VERIFY(transition->running()); + QTRY_VERIFY(!transition->running()); + QCOMPARE(child->scale(), qreal(1.0)); +} + #include "tst_qquickanimators.moc" QTEST_MAIN(tst_Animators) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 5d1897ab2d..8aeb449a85 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -2045,6 +2045,11 @@ void tst_qquickwindow::attachedProperty() QCOMPARE(text->property("contentItem").value<QQuickItem*>(), innerWindow->contentItem()); QCOMPARE(text->property("windowWidth").toInt(), innerWindow->width()); QCOMPARE(text->property("windowHeight").toInt(), innerWindow->height()); + + text->setParentItem(0); + QVERIFY(!text->property("contentItem").value<QQuickItem*>()); + QCOMPARE(text->property("windowWidth").toInt(), 0); + QCOMPARE(text->property("windowHeight").toInt(), 0); } class RenderJob : public QRunnable diff --git a/tests/manual/scenegraph_lancelot/data/text/textedit_multiline_selected_linebreaks_and_linewraps.qml b/tests/manual/scenegraph_lancelot/data/text/textedit_multiline_selected_linebreaks_and_linewraps.qml new file mode 100644 index 0000000000..a1004d0374 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/textedit_multiline_selected_linebreaks_and_linewraps.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +Item { + width: 200 + height: 480 + + TextEdit { + id: textEdit + anchors.centerIn: parent + font.family: "Arial" + font.pixelSize: 64 + width: 200 + textFormat: TextEdit.RichText + wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere + text: "ABC ABC<br>ABC" + + Component.onCompleted: { + textEdit.selectAll() + } + } + +} diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index af6b272fc6..dcb9709bb8 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -85,7 +85,7 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con if (!importNode->fileName.isEmpty()) { QString name = importNode->fileName.toString(); import[QStringLiteral("name")] = name; - if (name.endsWith(QStringLiteral(".js"))) { + if (name.endsWith(QLatin1String(".js"))) { import[QStringLiteral("type")] = QStringLiteral("javascript"); } else { import[QStringLiteral("type")] = QStringLiteral("directory"); @@ -188,7 +188,7 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) for (int i = 0; i < importsCopy.length(); ++i) { QVariantMap import = qvariant_cast<QVariantMap>(importsCopy[i]); - if (import[QStringLiteral("type")] == QStringLiteral("module")) { + if (import[QStringLiteral("type")] == QLatin1String("module")) { QString path = resolveImportPath(import.value(QStringLiteral("name")).toString(), import.value(QStringLiteral("version")).toString()); if (!path.isEmpty()) import[QStringLiteral("path")] = path; @@ -319,9 +319,9 @@ QVariantList findQmlImportsInFile(const QString &filePath) QFile f; if (f.open(stdin, QIODevice::ReadOnly)) imports = findQmlImportsInQmlCode(QLatin1String("<stdin>"), QString::fromUtf8(f.readAll())); - } else if (filePath.endsWith(QStringLiteral(".qml"))) { + } else if (filePath.endsWith(QLatin1String(".qml"))) { imports = findQmlImportsInQmlFile(filePath); - } else if (filePath.endsWith(QStringLiteral(".js"))) { + } else if (filePath.endsWith(QLatin1String(".js"))) { imports = findQmlImportsInJavascriptFile(filePath); } @@ -382,10 +382,10 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir) continue; // skip obvious build output directories - if (path.contains(QStringLiteral("Debug-iphoneos")) || path.contains(QStringLiteral("Release-iphoneos")) || - path.contains(QStringLiteral("Debug-iphonesimulator")) || path.contains(QStringLiteral("Release-iphonesimulator")) + if (path.contains(QLatin1String("Debug-iphoneos")) || path.contains(QLatin1String("Release-iphoneos")) || + path.contains(QLatin1String("Debug-iphonesimulator")) || path.contains(QLatin1String("Release-iphonesimulator")) #ifdef Q_OS_WIN - || path.contains(QStringLiteral("/release/")) || path.contains(QStringLiteral("/debug/")) + || path.contains(QLatin1String("/release/")) || path.contains(QLatin1String("/debug/")) #endif ){ continue; @@ -404,7 +404,7 @@ QSet<QString> importModulePaths(QVariantList imports) { QVariantMap import = qvariant_cast<QVariantMap>(importVariant); QString path = import.value(QStringLiteral("path")).toString(); QString type = import.value(QStringLiteral("type")).toString(); - if (type == QStringLiteral("module") && !path.isEmpty()) + if (type == QLatin1String("module") && !path.isEmpty()) ret.insert(QDir(path).canonicalPath()); } return ret; diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 776e19fdf4..7870e3a9df 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -51,9 +51,11 @@ #include <QtCore/private/qobject_p.h> #include <QtCore/private/qmetaobject_p.h> +#include <QRegularExpression> #include <iostream> #include <algorithm> +#include "qmltypereader.h" #include "qmlstreamwriter.h" #ifdef QT_SIMULATOR @@ -728,7 +730,7 @@ void sigSegvHandler(int) { void printUsage(const QString &appName) { std::cerr << qPrintable(QString( - "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies <dependencies.json>] module.uri version [module/import/path]\n" + "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies <dependencies.json>] [-merge <file-to-merge.qmltypes>] module.uri version [module/import/path]\n" " %1 [-v] [-noinstantiate] -path path/to/qmldir/directory [version]\n" " %1 [-v] -builtins\n" "Example: %1 Qt.labs.folderlistmodel 2.0 /home/user/dev/qt-install/imports").arg( @@ -982,6 +984,7 @@ int main(int argc, char *argv[]) QString pluginImportVersion; bool relocatable = true; QString dependenciesFile; + QString mergeFile; enum Action { Uri, Path, Builtins }; Action action = Uri; { @@ -1000,6 +1003,13 @@ int main(int argc, char *argv[]) return EXIT_INVALIDARGUMENTS; } dependenciesFile = args.at(iArg); + } else if (arg == QLatin1String("--merge") + || arg == QLatin1String("-merge")) { + if (++iArg == args.size()) { + std::cerr << "missing merge file" << std::endl; + return EXIT_INVALIDARGUMENTS; + } + mergeFile = args.at(iArg); } else if (arg == QLatin1String("--notrelocatable") || arg == QLatin1String("-notrelocatable") || arg == QLatin1String("--nonrelocatable") @@ -1061,6 +1071,26 @@ int main(int argc, char *argv[]) QDir::setCurrent(pluginImportPath); engine.addImportPath(pluginImportPath); } + + // Merge file. + QStringList mergeDependencies; + QString mergeComponents; + if (!mergeFile.isEmpty()) { + QStringList merge = readQmlTypes(mergeFile); + if (!merge.isEmpty()) { + QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)"); + QRegularExpressionMatchIterator i = re.globalMatch(merge[1]); + while (i.hasNext()) { + QRegularExpressionMatch m = i.next(); + QString d = m.captured(1); + mergeDependencies << m.captured(1); + } + mergeComponents = merge [2]; + } + } + + // Dependencies. + bool calculateDependencies = !pluginImportUri.isEmpty() && !pluginImportVersion.isEmpty(); QStringList dependencies; if (!dependenciesFile.isEmpty()) @@ -1210,6 +1240,13 @@ int main(int argc, char *argv[]) "// '%1 %2'\n" "\n").arg(QFileInfo(args.at(0)).baseName(), args.mid(1).join(QLatin1Char(' ')))); qml.writeStartObject("Module"); + + // Insert merge dependencies. + if (!mergeDependencies.isEmpty()) { + dependencies << mergeDependencies; + } + compactDependencies(&dependencies); + QStringList quotedDependencies; foreach (const QString &dep, dependencies) quotedDependencies << enquote(dep); @@ -1236,6 +1273,9 @@ int main(int argc, char *argv[]) if (pluginImportUri.isEmpty()) dumper.writeEasingCurve(); + // Insert merge file. + qml.write(mergeComponents); + qml.writeEndObject(); qml.writeEndDocument(); diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro index 6fdcd349d1..e45a7fad83 100644 --- a/tools/qmlplugindump/qmlplugindump.pro +++ b/tools/qmlplugindump/qmlplugindump.pro @@ -6,10 +6,12 @@ QTPLUGIN.platforms = qminimal SOURCES += \ main.cpp \ - qmlstreamwriter.cpp + qmlstreamwriter.cpp \ + qmltypereader.cpp HEADERS += \ - qmlstreamwriter.h + qmlstreamwriter.h \ + qmltypereader.h macx { # Prevent qmlplugindump from popping up in the dock when launched. diff --git a/tools/qmlplugindump/qmltypereader.cpp b/tools/qmlplugindump/qmltypereader.cpp new file mode 100644 index 0000000000..67ba415388 --- /dev/null +++ b/tools/qmlplugindump/qmltypereader.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmltypereader.h" + +#include <QFileInfo> +#include <QFile> +#include <QRegularExpression> + +#include <iostream> + +QStringList readQmlTypes(const QString &filename) { + QRegularExpression re("import QtQuick.tooling 1.2.*Module {\\s*dependencies:\\[([^\\]]*)\\](.*)}", + QRegularExpression::DotMatchesEverythingOption); + if (!QFileInfo(filename).exists()) { + std::cerr << "Non existing file: " << filename.toStdString() << std::endl; + return QStringList(); + } + QFile f(filename); + if (!f.open(QFileDevice::ReadOnly)) { + std::cerr << "Error in opening file " << filename.toStdString() << " : " + << f.errorString().toStdString() << std::endl; + return QStringList(); + } + QByteArray fileData = f.readAll(); + QString data(fileData); + QRegularExpressionMatch m = re.match(data); + if (m.lastCapturedIndex() != 2) { + std::cerr << "Malformed file: " << filename.toStdString() << std::endl; + return QStringList(); + } + return m.capturedTexts(); +} diff --git a/tools/qmlplugindump/qmltypereader.h b/tools/qmlplugindump/qmltypereader.h new file mode 100644 index 0000000000..b995566e0b --- /dev/null +++ b/tools/qmlplugindump/qmltypereader.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLTYPEREADER_H +#define QMLTYPEREADER_H + +#include <QString> +#include <QStringList> + +QStringList readQmlTypes(const QString &filename); + +#endif // QMLTYPEREADER_H |