diff options
-rw-r--r-- | src/imports/statemachine/plugin.cpp | 2 | ||||
-rw-r--r-- | src/imports/statemachine/signaltransition.cpp | 68 | ||||
-rw-r--r-- | src/imports/statemachine/signaltransition.h | 27 | ||||
-rw-r--r-- | src/qml/qml/qqmlboundsignal.cpp | 22 | ||||
-rw-r--r-- | src/qml/qml/qqmlboundsignal_p.h | 1 | ||||
-rw-r--r-- | src/quick/items/qquickitem.cpp | 2 | ||||
-rw-r--r-- | tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml | 79 | ||||
-rw-r--r-- | tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml | 80 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem2/data/qtbug_50516.qml | 9 | ||||
-rw-r--r-- | tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 22 | ||||
-rw-r--r-- | tools/qmlplugindump/main.cpp | 7 |
11 files changed, 314 insertions, 5 deletions
diff --git a/src/imports/statemachine/plugin.cpp b/src/imports/statemachine/plugin.cpp index 4a711a3278..2d52839f68 100644 --- a/src/imports/statemachine/plugin.cpp +++ b/src/imports/statemachine/plugin.cpp @@ -58,7 +58,7 @@ public: qmlRegisterUncreatableType<QState>(uri, 1, 0, "QState", "Don't use this, use State instead"); qmlRegisterUncreatableType<QAbstractState>(uri, 1, 0, "QAbstractState", "Don't use this, use State instead"); qmlRegisterUncreatableType<QSignalTransition>(uri, 1, 0, "QSignalTransition", "Don't use this, use SignalTransition instead"); - qmlRegisterType<SignalTransition>(uri, 1, 0, "SignalTransition"); + qmlRegisterCustomType<SignalTransition>(uri, 1, 0, "SignalTransition", new SignalTransitionParser); qmlRegisterType<TimeoutTransition>(uri, 1, 0, "TimeoutTransition"); qmlProtectModule(uri, 1); } diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 92650f7e07..33ee11cbe7 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -45,9 +45,10 @@ #include <private/qjsvalue_p.h> #include <private/qv4scopedvalue_p.h> #include <private/qqmlcontext_p.h> +#include <private/qqmlboundsignal_p.h> SignalTransition::SignalTransition(QState *parent) - : QSignalTransition(this, SIGNAL(invokeYourself()), parent) + : QSignalTransition(this, SIGNAL(invokeYourself()), parent), m_complete(false), m_signalExpression(Q_NULLPTR) { connect(this, SIGNAL(signalChanged()), SIGNAL(qmlSignalChanged())); } @@ -80,6 +81,15 @@ bool SignalTransition::eventTest(QEvent *event) return result.toBool(); } +void SignalTransition::onTransition(QEvent *event) +{ + if (m_signalExpression) { + QStateMachine::SignalEvent *e = static_cast<QStateMachine::SignalEvent*>(event); + m_signalExpression->evaluate(e->arguments()); + } + QSignalTransition::onTransition(event); +} + const QJSValue& SignalTransition::signal() { return m_signal; @@ -104,6 +114,8 @@ void SignalTransition::setSignal(const QJSValue &signal) QSignalTransition::setSenderObject(sender); QSignalTransition::setSignal(metaMethod.methodSignature()); + + connectTriggered(); } QQmlScriptString SignalTransition::guard() const @@ -125,6 +137,60 @@ void SignalTransition::invoke() emit invokeYourself(); } +void SignalTransition::connectTriggered() +{ + if (!m_complete || !m_cdata) + return; + + QObject *target = senderObject(); + QQmlData *ddata = QQmlData::get(this); + QQmlContextData *ctxtdata = ddata ? ddata->outerContext : 0; + + Q_ASSERT(m_bindings.count() == 1); + const QV4::CompiledData::Binding *binding = m_bindings.at(0); + Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); + + QV4::ExecutionEngine *jsEngine = QV8Engine::getV4(QQmlEngine::contextForObject(this)->engine()); + QV4::Scope scope(jsEngine); + QV4::Scoped<QV4::QObjectMethod> qobjectSignal(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal)); + Q_ASSERT(qobjectSignal); + QMetaMethod metaMethod = target->metaObject()->method(qobjectSignal->methodIndex()); + int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod); + + QQmlBoundSignalExpression *expression = ctxtdata ? + new QQmlBoundSignalExpression(target, signalIndex, + ctxtdata, this, m_cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : 0; + if (expression) + expression->setNotifyOnValueChanged(false); + m_signalExpression = expression; +} + +void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) +{ + for (int ii = 0; ii < props.count(); ++ii) { + const QV4::CompiledData::Binding *binding = props.at(ii); + + QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + + if (propName != QStringLiteral("onTriggered")) { + error(props.at(ii), SignalTransition::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return; + } + + if (binding->type != QV4::CompiledData::Binding::Type_Script) { + error(binding, SignalTransition::tr("SignalTransition: script expected")); + return; + } + } +} + +void SignalTransitionParser::applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) +{ + SignalTransition *st = qobject_cast<SignalTransition*>(object); + st->m_cdata = cdata; + st->m_bindings = bindings; +} + /*! \qmltype QAbstractTransition \inqmlmodule QtQml.StateMachine diff --git a/src/imports/statemachine/signaltransition.h b/src/imports/statemachine/signaltransition.h index 1b44af4de9..dfb1ca91b9 100644 --- a/src/imports/statemachine/signaltransition.h +++ b/src/imports/statemachine/signaltransition.h @@ -39,12 +39,17 @@ #include <QtQml/QJSValue> #include <QtQml/qqmlscriptstring.h> +#include <QtQml/qqmlparserstatus.h> +#include <private/qqmlcustomparser_p.h> +#include <private/qqmlboundsignalexpressionpointer_p.h> +#include <private/qqmlcompiler_p.h> QT_BEGIN_NAMESPACE -class SignalTransition : public QSignalTransition +class SignalTransition : public QSignalTransition, public QQmlParserStatus { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QJSValue signal READ signal WRITE setSignal NOTIFY qmlSignalChanged) Q_PROPERTY(QQmlScriptString guard READ guard WRITE setGuard NOTIFY guardChanged) @@ -54,7 +59,8 @@ public: QQmlScriptString guard() const; void setGuard(const QQmlScriptString &guard); - bool eventTest(QEvent *event); + bool eventTest(QEvent *event) Q_DECL_OVERRIDE; + void onTransition(QEvent *event) Q_DECL_OVERRIDE; const QJSValue &signal(); void setSignal(const QJSValue &signal); @@ -70,9 +76,24 @@ Q_SIGNALS: void qmlSignalChanged(); private: - QByteArray m_data; + void classBegin() Q_DECL_OVERRIDE { m_complete = false; } + void componentComplete() Q_DECL_OVERRIDE { m_complete = true; connectTriggered(); } + void connectTriggered(); + + friend class SignalTransitionParser; QJSValue m_signal; QQmlScriptString m_guard; + bool m_complete; + QQmlRefPointer<QQmlCompiledData> m_cdata; + QList<const QV4::CompiledData::Binding *> m_bindings; + QQmlBoundSignalExpressionPointer m_signalExpression; +}; + +class SignalTransitionParser : public QQmlCustomParser +{ +public: + void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) Q_DECL_OVERRIDE; + void applyBindings(QObject *object, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings) Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 477a517e32..6d8f883e4c 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -239,6 +239,28 @@ void QQmlBoundSignalExpression::evaluate(void **a) ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. } +void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args) +{ + Q_ASSERT (context() && engine()); + + if (!expressionFunctionValid()) + return; + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); + QV4::Scope scope(ep->v4engine()); + + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. + + QV4::ScopedCallData callData(scope, args.count()); + for (int ii = 0; ii < args.count(); ++ii) { + callData->args[ii] = scope.engine->fromVariant(args[ii]); + } + + QQmlJavaScriptExpression::evaluate(callData, 0); + + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. +} + //////////////////////////////////////////////////////////////////////// diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h index 3742317484..147752882d 100644 --- a/src/qml/qml/qqmlboundsignal_p.h +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -78,6 +78,7 @@ public: // evaluation of a bound signal expression doesn't return any value void evaluate(void **a); + void evaluate(const QList<QVariant> &args); QQmlSourceLocation sourceLocation() const; QString expression() const; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 224decefec..059bb4beb1 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2508,6 +2508,8 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo lastChild = prevTabChildItem(current, -1); } bool isTabFence = current->d_func()->isTabFence; + if (isTabFence && !hasChildren) + return current; // coming from parent: check children if (hasChildren && from == current->parentItem()) { diff --git a/tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml b/tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml new file mode 100644 index 0000000000..5d2e867da4 --- /dev/null +++ b/tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Ford Motor Company +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtTest 1.1 +import QtQml.StateMachine 1.0 + +TestCase { + id: testCase + + property string mystr + property bool mybool + property int myint + + StateMachine { + id: machine + initialState: startState + running: true + State { + id: startState + SignalTransition { + id: signalTrans + signal: testCase.mysignal + onTriggered: { + testCase.mystr = mystr + testCase.mybool = mybool + testCase.myint = myint + } + targetState: finalState + } + } + FinalState { + id: finalState + } + } + + signal mysignal(string mystr, bool mybool, int myint) + + name: "testTriggeredArguments1" + function test_triggeredArguments() + { + tryCompare(startState, "active", true) + + // Emit the signalTrans.signal + testCase.mysignal("test1", true, 2) + compare(testCase.mystr, "test1") + compare(testCase.mybool, true) + compare(testCase.myint, 2) + } +} diff --git a/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml new file mode 100644 index 0000000000..f60f2ff78c --- /dev/null +++ b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Ford Motor Company +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtTest 1.1 +import QtQml.StateMachine 1.0 + +TestCase { + id: testCase + + property string mystr + property bool mybool + property int myint + + StateMachine { + id: machine + initialState: startState + running: true + State { + id: startState + SignalTransition { + id: signalTrans + signal: testCase.mysignal + onTriggered: function(strarg, boolarg, intarg) { + testCase.mystr = strarg + testCase.mybool = boolarg + testCase.myint = intarg + } + targetState: finalState + } + } + FinalState { + id: finalState + } + } + + signal mysignal(string mystr, bool mybool, int myint) + + name: "testTriggeredArguments2" + function test_triggeredArguments() + { + tryCompare(startState, "active", true) + + // Emit the signalTrans.signal + testCase.mysignal("test1", true, 2) + expectFail("", "QTBUG-50328") + compare(testCase.mystr, "test1") + compare(testCase.mybool, true) + compare(testCase.myint, 2) + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516.qml new file mode 100644 index 0000000000..5e2a60b26f --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516.qml @@ -0,0 +1,9 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence { + objectName: "root" + focus: true + width: 800 + height: 600 +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index c7717b9cca..8a75cba84a 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -74,6 +74,7 @@ private slots: void nextItemInFocusChain3(); void tabFence(); + void qtbug_50516(); void keys(); void standardKeys_data(); @@ -1194,6 +1195,27 @@ void tst_QQuickItem::tabFence() verifyTabFocusChain(window, fence1BacktabFocusChain, false /* forward */); } +void tst_QQuickItem::qtbug_50516() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("qtbug_50516.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + QVERIFY(window->rootObject()->hasActiveFocus()); + + QQuickItem *contentItem = window->rootObject(); + QQuickItem *next = contentItem->nextItemInFocusChain(true); + QCOMPARE(next, contentItem); + next = contentItem->nextItemInFocusChain(false); + QCOMPARE(next, contentItem); + + delete window; +} + void tst_QQuickItem::keys() { QQuickView *window = new QQuickView(0); diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 070ea041d6..fe92f80bad 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -791,6 +791,13 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil << ": expected an array" << std::endl; return false; } + // Workaround for avoiding conflicting types when no dependency has been found. + // + // qmlplugindump used to import QtQuick, so all types defined in QtQuick used to be skipped when dumping. + // Now that it imports only Qt, it is no longer the case: if no dependency is found all the types defined + // in QtQuick will be dumped, causing conflicts. + if (dependencies->isEmpty()) + dependencies->push_back(QLatin1String("QtQuick 2.0")); return true; } |