Merge remote-tracking branch 'origin/5.15' into dev

Change-Id: I801567c11fcc3244a1ee7dabeb5079d49fc5c3a1
This commit is contained in:
Qt Forward Merge Bot 2019-09-22 22:29:02 +02:00
commit a510c9c9f4
31 changed files with 676 additions and 185 deletions

View File

@ -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));
}
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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();

View File

@ -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()

View File

@ -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

View File

@ -1,5 +1,5 @@
<RCC>
<qresource prefix="/">
<file alias="Retain.qml">data/Retain.qml</file>
<file alias="Retain.qml">Retain.qml</file>
</qresource>
</RCC>

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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
}
}
}

View File

@ -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()

View File

@ -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
}
}

View File

@ -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}\")

View 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);

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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();

View File

@ -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();
}

View File

@ -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'));
}
/*!

View File

@ -89,7 +89,7 @@ public:
typedef QFlags<ColorCodeComponent> ColorCode;
typedef QHash<int, ColorCode> ColorMapping;
ColorOutput();
ColorOutput(bool silent);
~ColorOutput();
void setColorMapping(const ColorMapping &cMapping);

View File

@ -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."));
}
}