aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp36
-rw-r--r--src/qmlmodels/qqmldelegatemodel_p_p.h3
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp3
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp7
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp69
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h6
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h7
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp9
-rw-r--r--src/quick/items/qquickitemsmodule.cpp3
-rw-r--r--src/quick/items/qquickwindow_p.h20
-rw-r--r--tests/auto/qml/qmlcachegen/data/retain.qrc (renamed from tests/auto/qml/qmlcachegen/retain.qrc)2
-rw-r--r--tests/auto/qml/qmlcachegen/qmlcachegen.pro2
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp3
-rw-r--r--tests/auto/qml/qmllint/tst_qmllint.cpp124
-rw-r--r--tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp99
-rw-r--r--tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml64
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp7
-rw-r--r--tests/manual/pointer/pointerDrag.qml65
-rw-r--r--tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in2
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp7
-rw-r--r--tools/qmlcachegen/qmlcachegen.pro1
-rw-r--r--tools/qmlcachegen/qtquickcompiler.prf12
-rw-r--r--tools/qmlcachegen/resourcefilter.cpp172
-rw-r--r--tools/qmllint/fakemetaobject.cpp14
-rw-r--r--tools/qmllint/fakemetaobject.h3
-rw-r--r--tools/qmllint/findunqualified.cpp37
-rw-r--r--tools/qmllint/findunqualified.h3
-rw-r--r--tools/qmllint/main.cpp5
-rw-r--r--tools/qmllint/qcoloroutput.cpp11
-rw-r--r--tools/qmllint/qcoloroutput_p.h2
-rw-r--r--tools/qmllint/qmljstypedescriptionreader.cpp51
31 files changed, 670 insertions, 179 deletions
diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp
index c37dc114a1..e2a750a43a 100644
--- a/src/qmlmodels/qqmldelegatemodel.cpp
+++ b/src/qmlmodels/qqmldelegatemodel.cpp
@@ -894,6 +894,7 @@ void PropertyUpdater::doUpdate()
auto sender = QObject::sender();
auto mo = sender->metaObject();
auto signalIndex = QObject::senderSignalIndex();
+ ++updateCount;
// start at 0 instead of propertyOffset to handle properties from parent hierarchy
for (auto i = 0; i < mo->propertyCount() + mo->propertyOffset(); ++i) {
auto property = mo->property(i);
@@ -907,6 +908,27 @@ void PropertyUpdater::doUpdate()
}
}
+void PropertyUpdater::breakBinding()
+{
+ auto it = senderToConnection.find(QObject::senderSignalIndex());
+ if (it == senderToConnection.end())
+ return;
+ if (updateCount == 0) {
+ QObject::disconnect(*it);
+ QQmlError warning;
+ warning.setUrl(qmlContext(QObject::sender())->baseUrl());
+ auto signalName = QString::fromLatin1(QObject::sender()->metaObject()->method(QObject::senderSignalIndex()).name());
+ signalName.chop(sizeof("changed")-1);
+ QString propName = signalName;
+ propName[0] = propName[0].toLower();
+ warning.setDescription(QString::fromUtf8("Writing to \"%1\" broke the binding to the underlying model").arg(propName));
+ qmlWarning(this, warning);
+ senderToConnection.erase(it);
+ } else {
+ --updateCount;
+ }
+}
+
void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *modelItemToIncubate, QObject *object)
{
auto incubatorPriv = QQmlIncubatorPrivate::get(this);
@@ -947,10 +969,18 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod
// only write to property if it was actually requested by the component
if (wasInRequired && prop.hasNotifySignal()) {
QMetaMethod changeSignal = prop.notifySignal();
- QMetaMethod updateSlot = PropertyUpdater::staticMetaObject.method(PropertyUpdater::staticMetaObject.indexOfSlot("doUpdate()"));
- QObject::connect(modelItemToIncubate, changeSignal, updater, updateSlot);
+ static QMetaMethod updateSlot = PropertyUpdater::staticMetaObject.method(PropertyUpdater::staticMetaObject.indexOfSlot("doUpdate()"));
+ QMetaObject::Connection conn = QObject::connect(modelItemToIncubate, changeSignal, updater, updateSlot);
+ auto propIdx = object->metaObject()->indexOfProperty(propName.toUtf8());
+ QMetaMethod writeToPropSignal = object->metaObject()->property(propIdx).notifySignal();
+ updater->senderToConnection[writeToPropSignal.methodIndex()] = conn;
+ static QMetaMethod breakBinding = PropertyUpdater::staticMetaObject.method(PropertyUpdater::staticMetaObject.indexOfSlot("breakBinding()"));
+ componentProp.write(prop.read(modelItemToIncubate));
+ // the connection needs to established after the write,
+ // else the signal gets triggered by it and breakBinding will remove the connection
+ QObject::connect(object, writeToPropSignal, updater, breakBinding);
}
- if (wasInRequired)
+ else if (wasInRequired) // we still have to write, even if there is no change signal
componentProp.write(prop.read(modelItemToIncubate));
}
}
diff --git a/src/qmlmodels/qqmldelegatemodel_p_p.h b/src/qmlmodels/qqmldelegatemodel_p_p.h
index f9dbc61a94..06365a212f 100644
--- a/src/qmlmodels/qqmldelegatemodel_p_p.h
+++ b/src/qmlmodels/qqmldelegatemodel_p_p.h
@@ -454,8 +454,11 @@ class PropertyUpdater : public QObject
public:
PropertyUpdater(QObject *parent);
+ QHash<int, QMetaObject::Connection> senderToConnection;
+ int updateCount = 0;
public Q_SLOTS:
void doUpdate();
+ void breakBinding();
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 61b66beff4..492897b68b 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -39,6 +39,7 @@
#include "qquickdraghandler_p.h"
#include <private/qquickwindow_p.h>
+#include <private/qquickmultipointhandler_p_p.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
@@ -228,7 +229,7 @@ void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
accumulatedDragDelta.setY(0);
}
qreal angle = std::atan2(accumulatedDragDelta.y(), accumulatedDragDelta.x()) * 180 / M_PI;
- bool overThreshold = QQuickWindowPrivate::dragOverThreshold(accumulatedDragDelta);
+ bool overThreshold = d_func()->dragOverThreshold(accumulatedDragDelta);
qCDebug(lcDragHandler) << "movement" << accumulatedDragDelta << "angle" << angle << "of point" << point
<< "pressed @" << point->scenePressPosition() << "over threshold?" << overThreshold;
minAngle = qMin(angle, minAngle);
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 9ef0f895ac..5b30d08557 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -43,6 +43,7 @@
#include <private/qsgadaptationlayer_p.h>
#include <private/qquickitem_p.h>
#include <private/qguiapplication_p.h>
+#include <private/qquickmultipointhandler_p_p.h>
#include <private/qquickwindow_p.h>
#include <QEvent>
#include <QMouseEvent>
@@ -338,8 +339,7 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
const QVector2D currentCentroid(centroid().scenePosition());
const QVector2D pressCentroid(centroid().scenePressPosition());
- QStyleHints *styleHints = QGuiApplication::styleHints();
- const int dragThreshold = styleHints->startDragDistance();
+ const int dragThreshold = QQuickPointerHandler::dragThreshold();
const int dragThresholdSquared = dragThreshold * dragThreshold;
double accumulatedCentroidDistance = 0; // Used to detect scale
@@ -399,7 +399,8 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
point->setAccepted(false); // don't stop propagation
setPassiveGrab(point);
}
- if (QQuickWindowPrivate::dragOverThreshold(point))
+ Q_D(QQuickMultiPointHandler);
+ if (d->dragOverThreshold(point))
++numberOfPointsDraggedOverThreshold;
}
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index ee0a8e7837..884ebd3b86 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -113,6 +113,47 @@ void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
}
/*!
+ \qmlproperty int PointerHandler::dragThreshold
+ \since 5.15
+
+ The distance in pixels that the user must drag an event point in order to
+ have it treated as a drag gesture.
+
+ The default value depends on the platform and screen resolution.
+ It can be reset back to the default value by setting it to undefined.
+ The behavior when a drag gesture begins varies in different handlers.
+*/
+int QQuickPointerHandler::dragThreshold() const
+{
+ Q_D(const QQuickPointerHandler);
+ if (d->dragThreshold < 0)
+ return qApp->styleHints()->startDragDistance();
+ return d->dragThreshold;
+}
+
+void QQuickPointerHandler::setDragThreshold(int t)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->dragThreshold == t)
+ return;
+
+ if (t > std::numeric_limits<qint16>::max())
+ qWarning() << "drag threshold cannot exceed" << std::numeric_limits<qint16>::max();
+ d->dragThreshold = qint16(t);
+ emit dragThresholdChanged();
+}
+
+void QQuickPointerHandler::resetDragThreshold()
+{
+ Q_D(QQuickPointerHandler);
+ if (d->dragThreshold < 0)
+ return;
+
+ d->dragThreshold = -1;
+ emit dragThresholdChanged();
+}
+
+/*!
Notification that the grab has changed in some way which is relevant to this handler.
The \a grabber (subject) will be the Input Handler whose state is changing,
or null if the state change regards an Item.
@@ -547,4 +588,32 @@ QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
{
}
+template <typename TEventPoint>
+bool QQuickPointerHandlerPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint *p) const
+{
+ Q_Q(const QQuickPointerHandler);
+ QStyleHints *styleHints = qApp->styleHints();
+ bool overThreshold = qAbs(d) > q->dragThreshold();
+ const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
+ if (!overThreshold && dragVelocityLimitAvailable) {
+ qreal velocity = qreal(axis == Qt::XAxis ? p->velocity().x() : p->velocity().y());
+ overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
+ }
+ return overThreshold;
+}
+
+bool QQuickPointerHandlerPrivate::dragOverThreshold(QVector2D delta) const
+{
+ Q_Q(const QQuickPointerHandler);
+ const float threshold = q->dragThreshold();
+ return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
+}
+
+bool QQuickPointerHandlerPrivate::dragOverThreshold(const QQuickEventPoint *point) const
+{
+ QPointF delta = point->scenePosition() - point->scenePressPosition();
+ return (dragOverThreshold(delta.x(), Qt::XAxis, point) ||
+ dragOverThreshold(delta.y(), Qt::YAxis, point));
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
index c600e42491..995db9c1dc 100644
--- a/src/quick/handlers/qquickpointerhandler_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -71,6 +71,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlP
Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT)
Q_PROPERTY(GrabPermissions grabPermissions READ grabPermissions WRITE setGrabPermissions NOTIFY grabPermissionChanged)
Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
+ Q_PROPERTY(int dragThreshold READ dragThreshold WRITE setDragThreshold RESET resetDragThreshold NOTIFY dragThresholdChanged REVISION 15)
public:
explicit QQuickPointerHandler(QQuickItem *parent = nullptr);
@@ -110,11 +111,16 @@ public:
qreal margin() const;
void setMargin(qreal pointDistanceThreshold);
+ int dragThreshold() const;
+ void setDragThreshold(int t);
+ void resetDragThreshold();
+
Q_SIGNALS:
void enabledChanged();
void activeChanged();
void targetChanged();
void marginChanged();
+ Q_REVISION(15) void dragThresholdChanged();
void grabChanged(QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point);
void grabPermissionChanged();
void canceled(QQuickEventPoint *point);
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
index 2ea4905643..5727b1ef55 100644
--- a/src/quick/handlers/qquickpointerhandler_p_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -68,9 +68,16 @@ public:
QQuickPointerHandlerPrivate();
+ template<typename TEventPoint>
+ bool dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint *p) const;
+
+ bool dragOverThreshold(QVector2D delta) const;
+ bool dragOverThreshold(const QQuickEventPoint *point) const;
+
QQuickPointerEvent *currentEvent = nullptr;
QQuickItem *target = nullptr;
qreal m_margin = 0;
+ qint16 dragThreshold = -1; // -1 means use the platform default
uint8_t grabPermissions : 8;
bool enabled : 1;
bool active : 1;
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index a10064a665..5f9d2151d7 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -97,13 +97,6 @@ QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
}
}
-static bool dragOverThreshold(const QQuickEventPoint *point)
-{
- QPointF delta = point->scenePosition() - point->scenePressPosition();
- return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
- QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
-}
-
bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
{
if (!point->pointerEvent()->asPointerMouseEvent() &&
@@ -115,7 +108,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
// (e.g. DragHandler) gets a chance to take over.
// Don't forget to emit released in case of a cancel.
bool ret = false;
- bool overThreshold = dragOverThreshold(point);
+ bool overThreshold = d_func()->dragOverThreshold(point);
if (overThreshold) {
m_longPressTimer.stop();
m_holdTimer.invalidate();
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 6db664f705..6a5b5c1a11 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -496,6 +496,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
QQuickPointerHandler::tr("ImageBase is an abstract base class"));
qmlRegisterType<QQuickImage, 14>(uri, 2, 14, "Image");
qmlRegisterType<QQuickDragHandler, 14>(uri, 2, 14, "DragHandler");
+
+ qmlRegisterUncreatableType<QQuickPointerHandler, 15>(uri, 2, 15, "PointerHandler",
+ QQuickPointerHandler::tr("PointerHandler is an abstract base class"));
}
static void initResources()
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index becbae7fe3..5d44e28bac 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -288,26 +288,6 @@ public:
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1);
- template <typename TEventPoint>
- static bool dragOverThreshold(qreal d, Qt::Axis axis, const TEventPoint *p, int startDragThreshold = -1)
- {
- QStyleHints *styleHints = qApp->styleHints();
- bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
- const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
- if (!overThreshold && dragVelocityLimitAvailable) {
- qreal velocity = axis == Qt::XAxis ? p->velocity().x() : p->velocity().y();
- overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
- }
- return overThreshold;
- }
-
- static bool dragOverThreshold(const QQuickEventPoint *point)
- {
- QPointF delta = point->scenePosition() - point->scenePressPosition();
- return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
- QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
- }
-
static bool dragOverThreshold(QVector2D delta);
// data property
diff --git a/tests/auto/qml/qmlcachegen/retain.qrc b/tests/auto/qml/qmlcachegen/data/retain.qrc
index e5eed9b12f..e1b9045fbe 100644
--- a/tests/auto/qml/qmlcachegen/retain.qrc
+++ b/tests/auto/qml/qmlcachegen/data/retain.qrc
@@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/">
- <file alias="Retain.qml">data/Retain.qml</file>
+ <file alias="Retain.qml">Retain.qml</file>
</qresource>
</RCC>
diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro
index 7bd4414302..18d3abd6bc 100644
--- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro
+++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro
@@ -25,7 +25,7 @@ workerscripts_test.prefix = /workerscripts
RESOURCES += \
workerscripts_test \
trickypaths.qrc \
- retain.qrc
+ data/retain.qrc
# QTBUG-46375
!win32: RESOURCES += trickypaths_umlaut.qrc
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 04f45bb902..7441aed11f 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -628,8 +628,7 @@ void tst_qmlcachegen::reproducibleCache_data()
QTest::addColumn<QString>("filePath");
QDir dir(dataDirectory());
- for (const QString &entry : dir.entryList(QDir::Files)) {
- QVERIFY(entry.endsWith(".qml") || entry.endsWith(".js") || entry.endsWith(".mjs"));
+ for (const QString &entry : dir.entryList((QStringList() << "*.qml" << "*.js" << "*.mjs"), QDir::Files)) {
QTest::newRow(entry.toUtf8().constData()) << dir.filePath(entry);
}
}
diff --git a/tests/auto/qml/qmllint/tst_qmllint.cpp b/tests/auto/qml/qmllint/tst_qmllint.cpp
index 2d225aebd3..9dc0a4dd42 100644
--- a/tests/auto/qml/qmllint/tst_qmllint.cpp
+++ b/tests/auto/qml/qmllint/tst_qmllint.cpp
@@ -38,16 +38,17 @@ class TestQmllint: public QQmlDataTest
private Q_SLOTS:
void initTestCase() override;
- void test();
- void test_data();
+
void testUnqualified();
void testUnqualified_data();
+
+ void cleanQmlCode_data();
+ void cleanQmlCode();
+
+ void dirtyQmlCode_data();
+ void dirtyQmlCode();
+
void testUnqualifiedNoSpuriousParentWarning();
- void catchIdentifierNoFalsePositive();
- void testUnmatchedSignalHandler();
- void uiQml();
- void methodInScope();
- void importWithPrefix();
private:
QString runQmllint(const QString &fileToLint, bool shouldSucceed);
@@ -68,24 +69,8 @@ void TestQmllint::initTestCase()
}
}
-void TestQmllint::test_data()
-{
- QTest::addColumn<QString>("filename");
- QTest::addColumn<bool>("isValid");
-
- // Valid files:
- QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml") << true;
- QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml") << true;
- QTest::newRow("QTBUG-45916_JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js") << true;
-
- // Invalid files:
- QTest::newRow("Invalid_syntax_QML") << QStringLiteral("failure1.qml") << false;
- QTest::newRow("Invalid_syntax_JS") << QStringLiteral("failure1.js") << false;
-}
-
void TestQmllint::testUnqualified()
{
- auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
QFETCH(QString, filename);
QFETCH(QString, warningMessage);
QFETCH(int, warningLine);
@@ -118,54 +103,66 @@ void TestQmllint::testUnqualified_data()
QTest::newRow("SignalHandlerShort2") << QStringLiteral("SignalHandler.qml") << QStringLiteral("onPressAndHold: (mouse) => {...") << 12 << 34;
// access catch identifier outside catch block
QTest::newRow("CatchStatement") << QStringLiteral("CatchStatement.qml") << QStringLiteral("err") << 6 << 21;
-}
-void TestQmllint::testUnqualifiedNoSpuriousParentWarning()
-{
- runQmllint("spuriousParentWarning.qml", true);
- runQmllint("nonSpuriousParentWarning.qml", false);
+ QTest::newRow("NonSpuriousParent") << QStringLiteral("nonSpuriousParentWarning.qml") << QStringLiteral("property int x: <id>.parent.x") << 6 << 25;
}
-void TestQmllint::catchIdentifierNoFalsePositive()
+void TestQmllint::testUnqualifiedNoSpuriousParentWarning()
{
- runQmllint("catchIdentifierNoWarning.qml", true);
+ const QString unknownNotFound = runQmllint("spuriousParentWarning.qml", true);
+ QVERIFY(unknownNotFound.contains(
+ QStringLiteral("warning: Unknown was not found. Did you add all import paths?")));
}
-void TestQmllint::testUnmatchedSignalHandler()
+void TestQmllint::dirtyQmlCode_data()
{
- const QString output = runQmllint("UnmatchedSignalHandler.qml", false);
- QVERIFY(output.contains(QString::asprintf(
- "Warning: no matching signal found for handler \"onClicked\" at %d:%d", 12, 13)));
- QVERIFY(!output.contains(QStringLiteral("onMouseXChanged")));
+ QTest::addColumn<QString>("filename");
+ QTest::addColumn<QString>("warningMessage");
+ QTest::addColumn<QString>("notContained");
+
+ QTest::newRow("Invalid_syntax_QML")
+ << QStringLiteral("failure1.qml")
+ << QStringLiteral("failure1.qml:4 : Expected token `:'")
+ << QString();
+ QTest::newRow("Invalid_syntax_JS")
+ << QStringLiteral("failure1.js")
+ << QStringLiteral("failure1.js:4 : Expected token `;'")
+ << QString();
+ QTest::newRow("UnmatchedSignalHandler")
+ << QStringLiteral("UnmatchedSignalHandler.qml")
+ << QString("Warning: no matching signal found for handler \"onClicked\" at 12:13")
+ << QStringLiteral("onMouseXChanged");
}
-void TestQmllint::uiQml()
+void TestQmllint::dirtyQmlCode()
{
- const QString output = runQmllint("FormUser.qml", true);
- QVERIFY(output.isEmpty());
-}
+ QFETCH(QString, filename);
+ QFETCH(QString, warningMessage);
+ QFETCH(QString, notContained);
-void TestQmllint::methodInScope()
-{
- const QString output = runQmllint("MethodInScope.qml", true);
- QVERIFY(output.isEmpty());
+ const QString output = runQmllint(filename, false);
+ QVERIFY(output.contains(warningMessage));
+ if (!notContained.isEmpty())
+ QVERIFY(!output.contains(notContained));
}
-void TestQmllint::importWithPrefix()
+void TestQmllint::cleanQmlCode_data()
{
- const QString output = runQmllint("ImportWithPrefix.qml", true);
- QVERIFY(output.isEmpty());
+ QTest::addColumn<QString>("filename");
+ QTest::newRow("Simple_QML") << QStringLiteral("Simple.qml");
+ QTest::newRow("QML_importing_JS") << QStringLiteral("importing_js.qml");
+ QTest::newRow("JS_with_pragma_and_import") << QStringLiteral("QTBUG-45916.js");
+ QTest::newRow("uiQml") << QStringLiteral("FormUser.qml");
+ QTest::newRow("methodInScope") << QStringLiteral("MethodInScope.qml");
+ QTest::newRow("importWithPrefix") << QStringLiteral("ImportWithPrefix.qml");
+ QTest::newRow("catchIdentifier") << QStringLiteral("catchIdentifierNoWarning.qml");
}
-void TestQmllint::test()
+void TestQmllint::cleanQmlCode()
{
QFETCH(QString, filename);
- QFETCH(bool, isValid);
- QStringList args;
- args << QStringLiteral("--silent") << testFile(filename);
-
- bool success = QProcess::execute(m_qmllintPath, args) == 0;
- QCOMPARE(success, isValid);
+ const QString warnings = runQmllint(filename, true);
+ QVERIFY(warnings.isEmpty());
}
QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed)
@@ -173,18 +170,27 @@ QString TestQmllint::runQmllint(const QString &fileToLint, bool shouldSucceed)
auto qmlImportDir = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
QStringList args;
args << QStringLiteral("-U") << testFile(fileToLint)
- << QStringLiteral("-I") << qmlImportDir;
- QProcess process;
- process.start(m_qmllintPath, args);
- [&]() {
+ << QStringLiteral("-I") << qmlImportDir
+ << QStringLiteral("--silent");
+ QString errors;
+ auto verify = [&](bool isSilent) {
+ QProcess process;
+ process.start(m_qmllintPath, args);
QVERIFY(process.waitForFinished());
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
if (shouldSucceed)
QCOMPARE(process.exitCode(), 0);
else
QVERIFY(process.exitCode() != 0);
- }();
- return process.readAllStandardError();
+ errors = process.readAllStandardError();
+
+ if (isSilent)
+ QVERIFY(errors.isEmpty());
+ };
+ verify(true);
+ args.removeLast();
+ verify(false);
+ return errors;
}
QTEST_MAIN(TestQmllint)
diff --git a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
index 66314f88a2..65c5ac9ef4 100644
--- a/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickdraghandler/tst_qquickdraghandler.cpp
@@ -53,9 +53,12 @@ private slots:
void initTestCase();
void defaultPropertyValues();
+ void touchDrag_data();
void touchDrag();
void mouseDrag_data();
void mouseDrag();
+ void mouseDragThreshold_data();
+ void mouseDragThreshold();
void dragFromMargin();
void snapMode_data();
void snapMode();
@@ -131,9 +134,18 @@ void tst_DragHandler::defaultPropertyValues()
QCOMPARE(dragHandler->centroid().sceneGrabPosition(), QPointF());
}
+void tst_DragHandler::touchDrag_data()
+{
+ QTest::addColumn<int>("dragThreshold");
+ QTest::newRow("threshold zero") << 0;
+ QTest::newRow("threshold one") << 1;
+ QTest::newRow("threshold 20") << 20;
+ QTest::newRow("threshold default") << -1;
+}
+
void tst_DragHandler::touchDrag()
{
- const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QFETCH(int, dragThreshold);
QScopedPointer<QQuickView> windowPtr;
createView(windowPtr, "draggables.qml");
QQuickView * window = windowPtr.data();
@@ -142,6 +154,12 @@ void tst_DragHandler::touchDrag()
QVERIFY(ball);
QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>();
QVERIFY(dragHandler);
+ if (dragThreshold < 0) {
+ dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QCOMPARE(dragHandler->dragThreshold(), dragThreshold);
+ } else {
+ dragHandler->setDragThreshold(dragThreshold);
+ }
QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
@@ -161,7 +179,9 @@ void tst_DragHandler::touchDrag()
p1 += QPoint(dragThreshold, 0);
QTest::touchEvent(window, touchDevice).move(1, p1, window);
QQuickTouchUtils::flush(window);
- QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0);
+ qCDebug(lcPointerTests) << "velocity after drag" << dragHandler->centroid().velocity();
+ if (dragThreshold > 0)
+ QTRY_VERIFY(!qFuzzyIsNull(dragHandler->centroid().velocity().x()));
QCOMPARE(centroidChangedSpy.count(), 2);
QVERIFY(!dragHandler->active());
p1 += QPoint(1, 0);
@@ -282,6 +302,81 @@ void tst_DragHandler::mouseDrag()
QCOMPARE(centroidChangedSpy.count(), shouldDrag ? 5 : 0);
}
+void tst_DragHandler::mouseDragThreshold_data()
+{
+ QTest::addColumn<int>("dragThreshold");
+ QTest::newRow("threshold zero") << 0;
+ QTest::newRow("threshold one") << 1;
+ QTest::newRow("threshold 20") << 20;
+ QTest::newRow("threshold default") << -1;
+}
+
+void tst_DragHandler::mouseDragThreshold()
+{
+ QFETCH(int, dragThreshold);
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "draggables.qml");
+ QQuickView * window = windowPtr.data();
+
+ QQuickItem *ball = window->rootObject()->childItems().first();
+ QVERIFY(ball);
+ QQuickDragHandler *dragHandler = ball->findChild<QQuickDragHandler*>();
+ QVERIFY(dragHandler);
+ if (dragThreshold < 0) {
+ dragThreshold = QGuiApplication::styleHints()->startDragDistance();
+ QCOMPARE(dragHandler->dragThreshold(), dragThreshold);
+ } else {
+ dragHandler->setDragThreshold(dragThreshold);
+ }
+
+ QSignalSpy translationChangedSpy(dragHandler, SIGNAL(translationChanged()));
+ QSignalSpy centroidChangedSpy(dragHandler, SIGNAL(centroidChanged()));
+
+ QPointF ballCenter = ball->clipRect().center();
+ QPointF scenePressPos = ball->mapToScene(ballCenter);
+ QPoint p1 = scenePressPos.toPoint();
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QVERIFY(!dragHandler->active());
+ QCOMPARE(dragHandler->centroid().position(), ballCenter);
+ QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
+ QCOMPARE(dragHandler->centroid().scenePosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().velocity(), QVector2D());
+ QCOMPARE(centroidChangedSpy.count(), 1);
+ p1 += QPoint(dragThreshold, 0);
+ QTest::mouseMove(window, p1);
+ if (dragThreshold > 0)
+ QTRY_VERIFY(dragHandler->centroid().velocity().x() > 0);
+ QCOMPARE(centroidChangedSpy.count(), 2);
+ QVERIFY(!dragHandler->active());
+ p1 += QPoint(1, 0);
+ QTest::mouseMove(window, p1);
+ QTRY_VERIFY(dragHandler->active());
+ QCOMPARE(translationChangedSpy.count(), 0);
+ QCOMPARE(centroidChangedSpy.count(), 3);
+ QCOMPARE(dragHandler->translation().x(), 0.0);
+ QPointF sceneGrabPos = p1;
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
+ p1 += QPoint(19, 0);
+ QTest::mouseMove(window, p1);
+ QTRY_VERIFY(dragHandler->active());
+ QCOMPARE(dragHandler->centroid().position(), ballCenter);
+ QCOMPARE(dragHandler->centroid().pressPosition(), ballCenter);
+ QCOMPARE(dragHandler->centroid().scenePosition(), ball->mapToScene(ballCenter));
+ QCOMPARE(dragHandler->centroid().scenePressPosition(), scenePressPos);
+ QCOMPARE(dragHandler->centroid().sceneGrabPosition(), sceneGrabPos);
+ QCOMPARE(dragHandler->translation().x(), dragThreshold + 20.0);
+ QCOMPARE(dragHandler->translation().y(), 0.0);
+ QVERIFY(dragHandler->centroid().velocity().x() > 0);
+ QCOMPARE(centroidChangedSpy.count(), 4);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_VERIFY(!dragHandler->active());
+ QCOMPARE(dragHandler->centroid().pressedButtons(), Qt::NoButton);
+ QCOMPARE(ball->mapToScene(ballCenter).toPoint(), p1);
+ QCOMPARE(translationChangedSpy.count(), 1);
+ QCOMPARE(centroidChangedSpy.count(), 5);
+}
+
void tst_DragHandler::dragFromMargin() // QTBUG-74966
{
const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
diff --git a/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml
new file mode 100644
index 0000000000..2996ba18fd
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/delegateWithRequiredProperties.3.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.14
+
+Item {
+ id: root
+ width: 800
+ height: 600
+ property bool working: false
+
+ ListModel {
+ id: myModel
+ ListElement {
+ name: "Bill Jones"
+ place: "Berlin"
+ }
+ ListElement {
+ name: "Jane Doe"
+ place: "Oslo"
+ }
+ ListElement {
+ name: "John Smith"
+ place: "Oulo"
+ }
+ }
+
+ Component {
+ id: delegateComponent
+ Rectangle {
+ id: myDelegate
+ height: 50
+ width: 50
+ required property string name
+ required property int index
+ onNameChanged: () => {if (myDelegate.name === "You-know-who") root.working = false}
+ Text {
+ text: myDelegate.name
+ font.pointSize: 10
+ anchors.fill: myDelegate
+ }
+ Component.onCompleted: () => {myDelegate.name = "break binding"}
+ }
+ }
+
+ PathView {
+ anchors.fill: parent
+ model: myModel
+ delegate: delegateComponent
+ path: Path {
+ startX: 80; startY: 100
+ PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 }
+ PathQuad { x: 140; y: 100; controlX: -20; controlY: 75 }
+ }
+ }
+ Timer {
+ interval: 1
+ running: true
+ repeat: false
+ onTriggered: () => {
+ myModel.setProperty(1, "name", "You-know-who")
+ myModel.setProperty(2, "name", "You-know-who")
+ root.working = true
+ }
+ }
+
+}
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 36a242bf3b..d2058cc8b3 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -2676,6 +2676,13 @@ void tst_QQuickPathView::requiredPropertiesInDelegate()
window->show();
QTRY_VERIFY(window->rootObject()->property("working").toBool());
}
+ {
+ QScopedPointer<QQuickView> window(createView());
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, QRegularExpression("Writing to \"name\" broke the binding to the underlying model"));
+ window->setSource(testFileUrl("delegateWithRequiredProperties.3.qml"));
+ window->show();
+ QTRY_VERIFY(window->rootObject()->property("working").toBool());
+ }
}
void tst_QQuickPathView::requiredPropertiesInDelegatePreventUnrelated()
diff --git a/tests/manual/pointer/pointerDrag.qml b/tests/manual/pointer/pointerDrag.qml
index 79044eb0b4..2063d8fae2 100644
--- a/tests/manual/pointer/pointerDrag.qml
+++ b/tests/manual/pointer/pointerDrag.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the manual tests of the Qt Toolkit.
@@ -26,7 +26,7 @@
**
****************************************************************************/
-import QtQuick 2.12
+import QtQuick 2.15
import "content"
Rectangle {
@@ -74,7 +74,14 @@ Rectangle {
label: "DragHandler"
objectName: "dragSquircle1"
DragHandler {
-
+ dragThreshold: ckZeroDragThreshold1.checked ? 0 : undefined
+ }
+ CheckBox {
+ id: ckZeroDragThreshold1
+ label: " Zero threshold"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 20
+ checked: false
}
}
@@ -99,7 +106,16 @@ Rectangle {
id: tap2
gesturePolicy: root.globalGesturePolicy
}
- DragHandler { }
+ DragHandler {
+ dragThreshold: ckZeroDragThreshold2.checked ? 0 : undefined
+ }
+ CheckBox {
+ id: ckZeroDragThreshold2
+ label: " Zero threshold"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 32
+ checked: false
+ }
}
TextBox {
@@ -107,7 +123,16 @@ Rectangle {
width: 100; height: 100
label: "DragHandler\nTapHandler"
color: queryColor(tap3.pressed)
- DragHandler { }
+ DragHandler {
+ dragThreshold: ckZeroDragThreshold3.checked ? 0 : undefined
+ }
+ CheckBox {
+ id: ckZeroDragThreshold3
+ label: " Zero threshold"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 32
+ checked: false
+ }
TapHandler {
id: tap3
gesturePolicy: root.globalGesturePolicy
@@ -118,7 +143,16 @@ Rectangle {
x: 400; y: 0
width: 100; height: 100
label: "DragHandler"
- DragHandler { }
+ DragHandler {
+ dragThreshold: ckZeroDragThreshold4.checked ? 0 : undefined
+ }
+ CheckBox {
+ id: ckZeroDragThreshold4
+ label: " Zero threshold"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 20
+ checked: false
+ }
TextBox {
label: "TapHandler"
@@ -145,6 +179,13 @@ Rectangle {
label: " Greedy ↓"
checked: true
}
+ CheckBox {
+ id: ckZeroDragThreshold5
+ label: " Zero threshold"
+ x: 10
+ anchors.bottom: ckGreedyDrag.top
+ checked: false
+ }
TapHandler {
id: tap5
gesturePolicy: root.globalGesturePolicy
@@ -159,6 +200,7 @@ Rectangle {
DragHandler {
grabPermissions: ckGreedyDrag ? DragHandler.CanTakeOverFromAnything :
DragHandler.CanTakeOverFromItems | DragHandler.CanTakeOverFromHandlersOfDifferentType | DragHandler.ApprovesTakeOverByAnything
+ dragThreshold: ckZeroDragThreshold5.checked ? 0 : undefined
}
}
}
@@ -174,7 +216,16 @@ Rectangle {
label: "DragHandler"
x: (parent.width - width)/2
y: 60
- DragHandler { }
+ DragHandler {
+ dragThreshold: ckZeroDragThreshold6.checked ? 0 : undefined
+ }
+ }
+ CheckBox {
+ id: ckZeroDragThreshold6
+ label: " Zero threshold"
+ anchors.horizontalCenter: parent.horizontalCenter
+ y: 20
+ checked: false
}
}
diff --git a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
index baa437a947..be2113b258 100644
--- a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
+++ b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake.in
@@ -50,7 +50,7 @@ but not all the files it references.
get_filename_component(input_resource ${_resource} ABSOLUTE)
- configure_file(${input_resource} ${new_resource_file} COPYONLY)
+ execute_process(COMMAND ${compiler_path} --filter-resource-file ${input_resource} -o ${new_resource_file} OUTPUT_VARIABLE remaining_files)
list(APPEND filtered_rcc_files ${new_resource_file})
list(APPEND loader_flags \"--resource-file-mapping=${_resource}=${new_resource_file}\")
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index 18415afd1d..b1670c940d 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -47,6 +47,7 @@
using namespace QQmlJS;
+int filterResourceFile(const QString &input, const QString &output);
bool generateLoader(const QStringList &compiledFiles, const QString &output,
const QStringList &resourceFileMappings, QString *errorString);
QString symbolNamespaceForPath(const QString &relativePath);
@@ -417,6 +418,8 @@ int main(int argc, char **argv)
parser.addHelpOption();
parser.addVersionOption();
+ QCommandLineOption filterResourceFileOption(QStringLiteral("filter-resource-file"), QCoreApplication::translate("main", "Filter out QML/JS files from a resource file that can be cached ahead of time instead"));
+ parser.addOption(filterResourceFileOption);
QCommandLineOption resourceFileMappingOption(QStringLiteral("resource-file-mapping"), QCoreApplication::translate("main", "Path from original resource file to new one"), QCoreApplication::translate("main", "old-name:new-name"));
parser.addOption(resourceFileMappingOption);
QCommandLineOption resourceOption(QStringLiteral("resource"), QCoreApplication::translate("main", "Qt resource file that might later contain one of the compiled files"), QCoreApplication::translate("main", "resource-file-name"));
@@ -462,6 +465,10 @@ int main(int argc, char **argv)
if (outputFileName.isEmpty())
outputFileName = inputFile + QLatin1Char('c');
+ if (parser.isSet(filterResourceFileOption)) {
+ return filterResourceFile(inputFile, outputFileName);
+ }
+
if (target == GenerateLoader) {
ResourceFileMapper mapper(sources);
diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro
index 910cb657d7..bee0b9a37e 100644
--- a/tools/qmlcachegen/qmlcachegen.pro
+++ b/tools/qmlcachegen/qmlcachegen.pro
@@ -4,6 +4,7 @@ QT = qmldevtools-private
DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES = qmlcachegen.cpp \
+ resourcefilter.cpp \
generateloader.cpp \
resourcefilemapper.cpp
TARGET = qmlcachegen
diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf
index 0129122157..a31a7f5714 100644
--- a/tools/qmlcachegen/qtquickcompiler.prf
+++ b/tools/qmlcachegen/qtquickcompiler.prf
@@ -1,5 +1,15 @@
if(qtc_run|lupdate_run): return()
+!contains(QT, qml) {
+ qt_modules = \
+ $$replace(QT, -private$, _private) \
+ $$replace(QT_PRIVATE, -private$, _private)
+ qt_modules = $$resolve_depends(qt_modules, "QT.", ".depends" ".run_depends")
+ !contains(qt_modules, qml): \
+ return()
+ unset(qt_modules)
+}
+
qtPrepareTool(QML_CACHEGEN, qmlcachegen, _FILTER)
qtPrepareTool(QMAKE_RCC, rcc, _DEP)
@@ -28,7 +38,7 @@ for(res, RESOURCES) {
contains(rccContents,.*\\.js$)|contains(rccContents,.*\\.qml$)|contains(rccContents,.*\\.mjs$) {
new_resource = $$qmlCacheResourceFileOutputName($$res)
mkpath($$dirname(new_resource))
- system($$QMAKE_QMAKE -install qinstall $$system_quote($$absRes) $$system_quote($$new_resource))
+ dummy = $$system($$QML_CACHEGEN_FILTER --filter-resource-file -o $$system_quote($$new_resource) $$system_quote($$absRes))
NEWRESOURCES += $$new_resource
QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes=$$new_resource)
diff --git a/tools/qmlcachegen/resourcefilter.cpp b/tools/qmlcachegen/resourcefilter.cpp
new file mode 100644
index 0000000000..261102dcbe
--- /dev/null
+++ b/tools/qmlcachegen/resourcefilter.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QString>
+#include <QXmlStreamReader>
+#include <QFile>
+#include <QDir>
+
+int filterResourceFile(const QString &input, const QString &output)
+{
+ enum State {
+ InitialState,
+ InRCC,
+ InResource,
+ InFile
+ };
+ State state = InitialState;
+
+ QString prefix;
+ QString currentFileName;
+ QXmlStreamAttributes fileAttributes;
+
+ QFile file(input);
+ if (!file.open(QIODevice::ReadOnly)) {
+ fprintf(stderr, "Cannot open %s for reading.\n", qPrintable(input));
+ return EXIT_FAILURE;
+ }
+
+ QDir inputDirectory = QFileInfo(file).absoluteDir();
+ QDir outputDirectory = QFileInfo(output).absoluteDir();
+
+ QString outputString;
+ QXmlStreamWriter writer(&outputString);
+ writer.setAutoFormatting(true);
+
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::StartDocument: {
+ QStringRef version = reader.documentVersion();
+ if (!version.isEmpty())
+ writer.writeStartDocument(version.toString());
+ else
+ writer.writeStartDocument();
+ break;
+ }
+ case QXmlStreamReader::EndDocument:
+ writer.writeEndDocument();
+ break;
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == QStringLiteral("RCC")) {
+ if (state != InitialState) {
+ fprintf(stderr, "Unexpected RCC tag in line %d\n", int(reader.lineNumber()));
+ return EXIT_FAILURE;
+ }
+ state = InRCC;
+ } else if (reader.name() == QStringLiteral("qresource")) {
+ if (state != InRCC) {
+ fprintf(stderr, "Unexpected qresource tag in line %d\n", int(reader.lineNumber()));
+ return EXIT_FAILURE;
+ }
+ state = InResource;
+ QXmlStreamAttributes attributes = reader.attributes();
+ if (attributes.hasAttribute(QStringLiteral("prefix")))
+ prefix = attributes.value(QStringLiteral("prefix")).toString();
+ if (!prefix.startsWith(QLatin1Char('/')))
+ prefix.prepend(QLatin1Char('/'));
+ if (!prefix.endsWith(QLatin1Char('/')))
+ prefix.append(QLatin1Char('/'));
+ } else if (reader.name() == QStringLiteral("file")) {
+ if (state != InResource) {
+ fprintf(stderr, "Unexpected file tag in line %d\n", int(reader.lineNumber()));
+ return EXIT_FAILURE;
+ }
+ state = InFile;
+ fileAttributes = reader.attributes();
+ continue;
+ }
+ writer.writeStartElement(reader.name().toString());
+ writer.writeAttributes(reader.attributes());
+ continue;
+
+ case QXmlStreamReader::EndElement:
+ if (reader.name() == QStringLiteral("file")) {
+ if (state != InFile) {
+ fprintf(stderr, "Unexpected end of file tag in line %d\n", int(reader.lineNumber()));
+ return EXIT_FAILURE;
+ }
+ state = InResource;
+ continue;
+ } else if (reader.name() == QStringLiteral("qresource")) {
+ if (state != InResource) {
+ fprintf(stderr, "Unexpected end of qresource tag in line %d\n", int(reader.lineNumber()));
+ return EXIT_FAILURE;
+ }
+ state = InRCC;
+ } else if (reader.name() == QStringLiteral("RCC")) {
+ if (state != InRCC) {
+ fprintf(stderr, "Unexpected end of RCC tag in line %d\n", int(reader.lineNumber()));
+ return EXIT_FAILURE;
+ }
+ state = InitialState;
+ }
+ writer.writeEndElement();
+ continue;
+
+ case QXmlStreamReader::Characters:
+ if (reader.isWhitespace())
+ break;
+ if (state != InFile)
+ return EXIT_FAILURE;
+ currentFileName = reader.text().toString();
+ if (currentFileName.isEmpty())
+ continue;
+
+ writer.writeStartElement(QStringLiteral("file"));
+
+ if (!fileAttributes.hasAttribute(QStringLiteral("alias")))
+ fileAttributes.append(QStringLiteral("alias"), currentFileName);
+
+ currentFileName = inputDirectory.absoluteFilePath(currentFileName);
+ currentFileName = outputDirectory.relativeFilePath(currentFileName);
+
+ writer.writeAttributes(fileAttributes);
+ writer.writeCharacters(currentFileName);
+ writer.writeEndElement();
+ continue;
+
+ default: break;
+ }
+ }
+
+ QFile outputFile(output);
+ if (!outputFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ fprintf(stderr, "Cannot open %s for writing.\n", qPrintable(output));
+ return EXIT_FAILURE;
+ }
+ const QByteArray outputStringUtf8 = outputString.toUtf8();
+ if (outputFile.write(outputStringUtf8) != outputStringUtf8.size())
+ return EXIT_FAILURE;
+
+ outputFile.close();
+ if (outputFile.error() != QFileDevice::NoError)
+ return EXIT_FAILURE;
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/qmllint/fakemetaobject.cpp b/tools/qmllint/fakemetaobject.cpp
index 514bb2fe42..8319ae6713 100644
--- a/tools/qmllint/fakemetaobject.cpp
+++ b/tools/qmllint/fakemetaobject.cpp
@@ -46,8 +46,8 @@ QString FakeMetaEnum::name() const
void FakeMetaEnum::setName(const QString &name)
{ m_name = name; }
-void FakeMetaEnum::addKey(const QString &key, int value)
-{ m_keys.append(key); m_values.append(value); }
+void FakeMetaEnum::addKey(const QString &key)
+{ m_keys.append(key); }
QString FakeMetaEnum::key(int index) const
{ return m_keys.at(index); }
@@ -73,10 +73,6 @@ void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
}
- len = m_values.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- foreach (int value, m_values)
- hash.addData(reinterpret_cast<const char *>(&value), sizeof(value));
}
QString FakeMetaEnum::describe(int baseIndent) const
@@ -84,16 +80,14 @@ QString FakeMetaEnum::describe(int baseIndent) const
QString newLine = QString::fromLatin1("\n") + QString::fromLatin1(" ").repeated(baseIndent);
QString res = QLatin1String("Enum ");
res += name();
- res += QLatin1String(":{");
+ res += QLatin1String(": [");
for (int i = 0; i < keyCount(); ++i) {
res += newLine;
res += QLatin1String(" ");
res += key(i);
- res += QLatin1String(": ");
- res += QString::number(m_values.value(i, -1));
}
res += newLine;
- res += QLatin1Char('}');
+ res += QLatin1Char(']');
return res;
}
diff --git a/tools/qmllint/fakemetaobject.h b/tools/qmllint/fakemetaobject.h
index 4e0ea1f8b3..ae76596343 100644
--- a/tools/qmllint/fakemetaobject.h
+++ b/tools/qmllint/fakemetaobject.h
@@ -46,7 +46,6 @@ namespace LanguageUtils {
class FakeMetaEnum {
QString m_name;
QStringList m_keys;
- QList<int> m_values;
public:
FakeMetaEnum();
@@ -57,7 +56,7 @@ public:
QString name() const;
void setName(const QString &name);
- void addKey(const QString &key, int value);
+ void addKey(const QString &key);
QString key(int index) const;
int keyCount() const;
QStringList keys() const;
diff --git a/tools/qmllint/findunqualified.cpp b/tools/qmllint/findunqualified.cpp
index 4404ddf49a..52cc36aeeb 100644
--- a/tools/qmllint/findunqualified.cpp
+++ b/tools/qmllint/findunqualified.cpp
@@ -40,8 +40,6 @@
#include <private/qqmljsparser_p.h>
#include <private/qv4codegen_p.h>
-QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc);
-
static QQmlJS::TypeDescriptionReader createReaderForFile(QString const &filename)
{
QFile f(filename);
@@ -139,9 +137,8 @@ void FindUnqualifiedIDVisitor::importHelper(QString id, QString prefix, int majo
if (QFile::exists(qmltypesPath)) {
auto reader = createReaderForFile(qmltypesPath);
auto succ = reader(&objects, &moduleApis, &dependencies);
- if (!succ) {
- qDebug() << reader.errorMessage();
- }
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
break;
}
}
@@ -292,13 +289,17 @@ FindUnqualifiedIDVisitor::localQmlFile2FakeMetaObject(QString filePath)
} else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
// nothing to do
} else {
- qDebug() << "unsupportedd sourceElement at" << sourceElement->firstSourceLocation()
- << sourceElement->sourceElement->kind;
+ const auto loc = sourceElement->firstSourceLocation();
+ m_colorOut.writeUncolored(
+ "unsupportedd sourceElement at "
+ + QString::fromLatin1("%1:%2: ").arg(loc.startLine).arg(loc.startColumn)
+ + QString::number(sourceElement->sourceElement->kind));
}
break;
}
default: {
- qDebug() << "unsupported element of kind" << initMembers->member->kind;
+ m_colorOut.writeUncolored("unsupported element of kind "
+ + QString::number(initMembers->member->kind));
}
}
initMembers = initMembers->next;
@@ -368,9 +369,8 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::UiProgram *)
while (it.hasNext()) {
auto reader = createReaderForFile(it.next());
auto succ = reader(&objects, &moduleApis, &dependencies);
- if (!succ) {
- qDebug() << reader.errorMessage();
- }
+ if (!succ)
+ m_colorOut.writeUncolored(reader.errorMessage());
}
}
// add builtins
@@ -578,13 +578,15 @@ bool FindUnqualifiedIDVisitor::visit(QQmlJS::AST::IdentifierExpression *idexp)
}
FindUnqualifiedIDVisitor::FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs,
- const QString &code, const QString &fileName)
+ const QString &code, const QString &fileName,
+ bool silent)
: m_rootScope(new ScopeTree { ScopeType::JSFunctionScope, "global" }),
m_currentScope(m_rootScope.get()),
m_qmltypeDirs(qmltypeDirs),
m_code(code),
m_rootId(QLatin1String("<id>")),
- m_filePath(fileName)
+ m_filePath(fileName),
+ m_colorOut(silent)
{
// setup color output
m_colorOut.insertColorMapping(Error, ColorOutput::RedForeground);
@@ -806,12 +808,3 @@ void FindUnqualifiedIDVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
{
leaveEnvironment();
}
-
-QDebug operator<<(QDebug dbg, const QQmlJS::AST::SourceLocation &loc)
-{
- QDebugStateSaver saver(dbg);
- dbg.nospace() << loc.startLine;
- dbg.nospace() << ":";
- dbg.nospace() << loc.startColumn;
- return dbg.maybeSpace();
-}
diff --git a/tools/qmllint/findunqualified.h b/tools/qmllint/findunqualified.h
index f7d1aab1f4..98e14f8dc7 100644
--- a/tools/qmllint/findunqualified.h
+++ b/tools/qmllint/findunqualified.h
@@ -43,7 +43,8 @@ enum class ScopeType;
class FindUnqualifiedIDVisitor : public QQmlJS::AST::Visitor {
public:
- explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code, const QString& fileName);
+ explicit FindUnqualifiedIDVisitor(QStringList const &qmltypeDirs, const QString& code,
+ const QString& fileName, bool silent);
~FindUnqualifiedIDVisitor() override;
bool check();
diff --git a/tools/qmllint/main.cpp b/tools/qmllint/main.cpp
index 235ec16c6e..0fa2ab53e4 100644
--- a/tools/qmllint/main.cpp
+++ b/tools/qmllint/main.cpp
@@ -50,7 +50,8 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
- qWarning() << "Failed to open file" << filename << file.error();
+ if (!silent)
+ qWarning() << "Failed to open file" << filename << file.error();
return false;
}
@@ -76,7 +77,7 @@ static bool lint_file(const QString &filename, const bool silent, const bool war
if (success && !isJavaScript && warnUnqualied) {
auto root = parser.rootNode();
- FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename};
+ FindUnqualifiedIDVisitor v { qmltypeDirs, code, filename, silent };
root->accept(&v);
success = v.check();
}
diff --git a/tools/qmllint/qcoloroutput.cpp b/tools/qmllint/qcoloroutput.cpp
index d2e723700a..eb4c721663 100644
--- a/tools/qmllint/qcoloroutput.cpp
+++ b/tools/qmllint/qcoloroutput.cpp
@@ -39,7 +39,7 @@
class ColorOutputPrivate
{
public:
- ColorOutputPrivate() : currentColorID(-1)
+ ColorOutputPrivate(bool silent) : currentColorID(-1), silent(silent)
{
/* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance
@@ -53,6 +53,7 @@ public:
ColorOutput::ColorMapping colorMapping;
int currentColorID;
bool coloringEnabled;
+ bool silent;
static const char *const foregrounds[];
static const char *const backgrounds[];
@@ -238,7 +239,7 @@ ColorOutput::ColorMapping ColorOutput::colorMapping() const
/*!
Constructs a ColorOutput instance, ready for use.
*/
-ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
+ColorOutput::ColorOutput(bool silent) : d(new ColorOutputPrivate(silent))
{
}
@@ -258,7 +259,8 @@ ColorOutput::~ColorOutput() = default; // must be here so that QScopedPointer ha
*/
void ColorOutput::write(const QString &message, int colorID)
{
- d->write(colorify(message, colorID));
+ if (!d->silent)
+ d->write(colorify(message, colorID));
}
/*!
@@ -269,7 +271,8 @@ void ColorOutput::write(const QString &message, int colorID)
*/
void ColorOutput::writeUncolored(const QString &message)
{
- d->write(message + QLatin1Char('\n'));
+ if (!d->silent)
+ d->write(message + QLatin1Char('\n'));
}
/*!
diff --git a/tools/qmllint/qcoloroutput_p.h b/tools/qmllint/qcoloroutput_p.h
index 710bf5db74..aefa765a87 100644
--- a/tools/qmllint/qcoloroutput_p.h
+++ b/tools/qmllint/qcoloroutput_p.h
@@ -89,7 +89,7 @@ public:
typedef QFlags<ColorCodeComponent> ColorCode;
typedef QHash<int, ColorCode> ColorMapping;
- ColorOutput();
+ ColorOutput(bool silent);
~ColorOutput();
void setColorMapping(const ColorMapping &cMapping);
diff --git a/tools/qmllint/qmljstypedescriptionreader.cpp b/tools/qmllint/qmljstypedescriptionreader.cpp
index 542cdf99eb..44a0d6f8b7 100644
--- a/tools/qmllint/qmljstypedescriptionreader.cpp
+++ b/tools/qmllint/qmljstypedescriptionreader.cpp
@@ -666,39 +666,34 @@ void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUt
return;
}
- ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
+ auto *expStmt = AST::cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
- addError(ast->statement->firstSourceLocation(), tr("Expected object literal after colon."));
- return;
- }
-
- ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
- if (!objectLit) {
- addError(expStmt->firstSourceLocation(), tr("Expected object literal after colon."));
+ addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
return;
}
- for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
- PatternProperty *assignement = AST::cast<PatternProperty *>(it->property);
- if (assignement) {
- StringLiteralPropertyName *propName = AST::cast<StringLiteralPropertyName *>(assignement->name);
- NumericLiteral *value = AST::cast<NumericLiteral *>(assignement->initializer);
- UnaryMinusExpression *minus = AST::cast<UnaryMinusExpression *>(assignement->initializer);
- if (minus)
- value = AST::cast<NumericLiteral *>(minus->expression);
- if (!propName || !value) {
- addError(objectLit->firstSourceLocation(), tr("Expected object literal to contain only 'string: number' elements."));
- continue;
+ if (auto *objectLit = AST::cast<ObjectPattern *>(expStmt->expression)) {
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ if (PatternProperty *assignement = it->property) {
+ if (auto *name = AST::cast<StringLiteralPropertyName *>(assignement->name)) {
+ fme->addKey(name->id.toString());
+ continue;
+ }
}
-
- double v = value->value;
- if (minus)
- v = -v;
- fme->addKey(propName->id.toString(), v);
- continue;
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
+ }
+ } else if (auto *arrayLit = AST::cast<ArrayPattern *>(expStmt->expression)) {
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ if (PatternElement *element = it->element) {
+ if (auto *name = AST::cast<StringLiteral *>(element->initializer)) {
+ fme->addKey(name->value.toString());
+ continue;
+ }
+ }
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
}
- PatternPropertyList *getterSetter = AST::cast<PatternPropertyList *>(it->next);
- if (getterSetter)
- addError(objectLit->firstSourceLocation(), tr("Enum should not contain getter and setters, but only 'string: number' elements."));
+ } else {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected either array or object literal as enum definition."));
}
}