Merge remote-tracking branch 'origin/5.15' into dev
Change-Id: I801567c11fcc3244a1ee7dabeb5079d49fc5c3a1
This commit is contained in:
commit
a510c9c9f4
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,47 @@ void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
|
|||
emit marginChanged();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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,
|
||||
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="Retain.qml">data/Retain.qml</file>
|
||||
<file alias="Retain.qml">Retain.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
QTest::newRow("NonSpuriousParent") << QStringLiteral("nonSpuriousParentWarning.qml") << QStringLiteral("property int x: <id>.parent.x") << 6 << 25;
|
||||
}
|
||||
|
||||
void TestQmllint::testUnqualifiedNoSpuriousParentWarning()
|
||||
{
|
||||
runQmllint("spuriousParentWarning.qml", true);
|
||||
runQmllint("nonSpuriousParentWarning.qml", false);
|
||||
const QString unknownNotFound = runQmllint("spuriousParentWarning.qml", true);
|
||||
QVERIFY(unknownNotFound.contains(
|
||||
QStringLiteral("warning: Unknown was not found. Did you add all import paths?")));
|
||||
}
|
||||
|
||||
void TestQmllint::catchIdentifierNoFalsePositive()
|
||||
void TestQmllint::dirtyQmlCode_data()
|
||||
{
|
||||
runQmllint("catchIdentifierNoWarning.qml", true);
|
||||
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::testUnmatchedSignalHandler()
|
||||
{
|
||||
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")));
|
||||
}
|
||||
|
||||
void TestQmllint::uiQml()
|
||||
{
|
||||
const QString output = runQmllint("FormUser.qml", true);
|
||||
QVERIFY(output.isEmpty());
|
||||
}
|
||||
|
||||
void TestQmllint::methodInScope()
|
||||
{
|
||||
const QString output = runQmllint("MethodInScope.qml", true);
|
||||
QVERIFY(output.isEmpty());
|
||||
}
|
||||
|
||||
void TestQmllint::importWithPrefix()
|
||||
{
|
||||
const QString output = runQmllint("ImportWithPrefix.qml", true);
|
||||
QVERIFY(output.isEmpty());
|
||||
}
|
||||
|
||||
void TestQmllint::test()
|
||||
void TestQmllint::dirtyQmlCode()
|
||||
{
|
||||
QFETCH(QString, filename);
|
||||
QFETCH(bool, isValid);
|
||||
QStringList args;
|
||||
args << QStringLiteral("--silent") << testFile(filename);
|
||||
QFETCH(QString, warningMessage);
|
||||
QFETCH(QString, notContained);
|
||||
|
||||
bool success = QProcess::execute(m_qmllintPath, args) == 0;
|
||||
QCOMPARE(success, isValid);
|
||||
const QString output = runQmllint(filename, false);
|
||||
QVERIFY(output.contains(warningMessage));
|
||||
if (!notContained.isEmpty())
|
||||
QVERIFY(!output.contains(notContained));
|
||||
}
|
||||
|
||||
void TestQmllint::cleanQmlCode_data()
|
||||
{
|
||||
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::cleanQmlCode()
|
||||
{
|
||||
QFETCH(QString, filename);
|
||||
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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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}\")
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
typedef QFlags<ColorCodeComponent> ColorCode;
|
||||
typedef QHash<int, ColorCode> ColorMapping;
|
||||
|
||||
ColorOutput();
|
||||
ColorOutput(bool silent);
|
||||
~ColorOutput();
|
||||
|
||||
void setColorMapping(const ColorMapping &cMapping);
|
||||
|
|
|
@ -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."));
|
||||
addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectPattern *objectLit = AST::cast<ObjectPattern *>(expStmt->expression);
|
||||
if (!objectLit) {
|
||||
addError(expStmt->firstSourceLocation(), tr("Expected object literal 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."));
|
||||
}
|
||||
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 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."));
|
||||
}
|
||||
} else {
|
||||
addError(ast->statement->firstSourceLocation(),
|
||||
tr("Expected either array or object literal as enum definition."));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue