diff options
39 files changed, 711 insertions, 327 deletions
diff --git a/examples/quick/demos/photoviewer/main.qml b/examples/quick/demos/photoviewer/main.qml index f443b70762..cbafb0112a 100644 --- a/examples/quick/demos/photoviewer/main.qml +++ b/examples/quick/demos/photoviewer/main.qml @@ -49,6 +49,15 @@ ApplicationWindow { visible: true + Rectangle { + focus: true + + Keys.onBackPressed: { + event.accepted = true + backButton.clicked() + } + } + property real downloadProgress: 0 property bool imageLoading: false property bool editMode: false @@ -99,7 +108,14 @@ ApplicationWindow { ListView { anchors.fill: parent; model: albumVisualModel.parts.browser; interactive: false } - Button { id: backButton; label: qsTr("Back"); rotation: 3; x: parent.width - backButton.width - 6; y: -backButton.height - 8 } + Button { + id: backButton + label: qsTr("Back") + rotation: 3 + x: parent.width - backButton.width - 6 + y: -backButton.height - 8 + visible: Qt.platform.os !== "android" + } Rectangle { id: photosShade; color: 'black'; width: parent.width; height: parent.height; opacity: 0; visible: opacity != 0.0 } diff --git a/examples/quick/demos/samegame/samegame.pro b/examples/quick/demos/samegame/samegame.pro index 59cacd3c8b..b9b316871c 100644 --- a/examples/quick/demos/samegame/samegame.pro +++ b/examples/quick/demos/samegame/samegame.pro @@ -6,3 +6,5 @@ RESOURCES += samegame.qrc target.path = $$[QT_INSTALL_EXAMPLES]/quick/demos/samegame INSTALLS += target + +QTPLUGIN += qsqlite diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index 7a7649c6f1..d5dbeeb4c2 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -694,7 +694,6 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) V4THROW_SQL2(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("SQL: database version mismatch")); } else { created = !QFile::exists(basename+QLatin1String(".sqlite")); - database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); if (created) { ini.setValue(QLatin1String("Name"), dbname); if (dbcreationCallback) @@ -710,6 +709,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) } version = ini.value(QLatin1String("Version")).toString(); } + database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); database.setDatabaseName(basename+QLatin1String(".sqlite")); } if (!database.isOpen()) diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp index 0fe0b2ae0b..1f357df20d 100644 --- a/src/particles/qquickitemparticle.cpp +++ b/src/particles/qquickitemparticle.cpp @@ -163,6 +163,7 @@ void QQuickItemParticle::tick(int time) m_deletables.clear(); foreach (QQuickParticleData* d, m_loadables){ + Q_ASSERT(d); if (m_stasis.contains(d->delegate)) qWarning() << "Current model particles prefers overwrite:false"; //remove old item from the particle that is dying to make room for this one diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 45fdab3fd1..3aad050dff 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1746,10 +1746,21 @@ QString QQmlPropertyValidator::bindingAsString(int objectIndex, const QV4::Compi typedef QVarLengthArray<const QV4::CompiledData::Binding *, 8> GroupPropertyVector; -static bool compareNameIndices(const QV4::CompiledData::Binding *binding, quint32 name) +struct BindingFinder { - return binding->propertyNameIndex < name; -} + bool operator()(quint32 name, const QV4::CompiledData::Binding *binding) const + { + return name < binding->propertyNameIndex; + } + bool operator()(const QV4::CompiledData::Binding *binding, quint32 name) const + { + return binding->propertyNameIndex < name; + } + bool operator()(const QV4::CompiledData::Binding *lhs, const QV4::CompiledData::Binding *rhs) const + { + return lhs->propertyNameIndex < rhs->propertyNameIndex; + } +}; bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) { @@ -1797,7 +1808,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD return false; } - GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, compareNameIndices); + GroupPropertyVector::const_iterator pos = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); groupProperties.insert(pos, binding); } @@ -1910,7 +1921,7 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD } if (pd) { - GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, compareNameIndices); + GroupPropertyVector::const_iterator assignedGroupProperty = std::lower_bound(groupProperties.constBegin(), groupProperties.constEnd(), binding->propertyNameIndex, BindingFinder()); const bool assigningToGroupProperty = assignedGroupProperty != groupProperties.constEnd() && !(binding->propertyNameIndex < (*assignedGroupProperty)->propertyNameIndex); if (!pd->isWritable() diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 6ee23690a6..5fb94af140 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -69,6 +69,10 @@ struct Function; struct Lookup; struct RegExp; +#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) +#pragma pack(push, 1) +#endif + struct Location { qint32 line; @@ -82,29 +86,6 @@ struct Location } }; -struct TypeReference -{ - TypeReference(const Location &loc) - : location(loc) - , needsCreation(false) - , errorWhenNotFound(false) - {} - Location location; // first use - bool needsCreation : 1; // whether the type needs to be creatable or not - bool errorWhenNotFound: 1; -}; - -// map from name index to location of first use -struct TypeReferenceMap : QHash<int, TypeReference> -{ - TypeReference &add(int nameIndex, const Location &loc) { - Iterator it = find(nameIndex); - if (it != end()) - return *it; - return *insert(nameIndex, loc); - } -}; - struct RegExp { enum Flags { @@ -553,6 +534,33 @@ struct QmlUnit } }; +#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) +#pragma pack(pop) +#endif + +struct TypeReference +{ + TypeReference(const Location &loc) + : location(loc) + , needsCreation(false) + , errorWhenNotFound(false) + {} + Location location; // first use + bool needsCreation : 1; // whether the type needs to be creatable or not + bool errorWhenNotFound: 1; +}; + +// map from name index to location of first use +struct TypeReferenceMap : QHash<int, TypeReference> +{ + TypeReference &add(int nameIndex, const Location &loc) { + Iterator it = find(nameIndex); + if (it != end()) + return *it; + return *insert(nameIndex, loc); + } +}; + // This is how this hooks into the existing structures: //VM::Function diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index c0b21a943f..bde61dca9d 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -672,6 +672,8 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, exists = QQmlFile::bundleFileExists(qmlUrl, typeLoader->engine()); } else { exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(qmlUrl)).isEmpty(); + if (!exists) + exists = QQmlMetaType::findCachedCompilationUnit(QUrl(qmlUrl)); } if (exists) { @@ -1849,17 +1851,21 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b // This is an 'identified' module if (typeNamespace != uri) { // The namespace for type registrations must match the URI for locating the module - QQmlError error; - error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri)); - errors->prepend(error); + if (errors) { + QQmlError error; + error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri)); + errors->prepend(error); + } return false; } if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace)) { // Other modules have already installed to this namespace - QQmlError error; - error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace)); - errors->prepend(error); + if (errors) { + QQmlError error; + error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace)); + errors->prepend(error); + } return false; } else { QQmlMetaType::protectNamespace(typeNamespace); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index efdf4d86da..65e302d3d9 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -651,7 +651,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) QString id = stringAt(_compiledObject->idIndex); if (!id.isEmpty()) { QQmlPropertyData *idProperty = _propertyCache->property(QStringLiteral("id"), _qobject, context); - if (idProperty && idProperty->isWritable()) { + if (idProperty && idProperty->isWritable() && idProperty->propType == QMetaType::QString) { QV4::CompiledData::Binding idBinding; idBinding.propertyNameIndex = 0; // Not used idBinding.flags = 0; diff --git a/src/qml/qml/qqmlscriptstring.cpp b/src/qml/qml/qqmlscriptstring.cpp index fd710df52f..af9d8ec265 100644 --- a/src/qml/qml/qqmlscriptstring.cpp +++ b/src/qml/qml/qqmlscriptstring.cpp @@ -126,7 +126,9 @@ Returns whether the QQmlScriptString is empty. */ bool QQmlScriptString::isEmpty() const { - return d->script.isEmpty(); + if (!d->script.isEmpty()) + return false; + return d->bindingId == -1; } /*! diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index d0d6839bdc..a7db7d214e 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -980,12 +980,13 @@ ReturnedValue QtObject::method_createQmlObject(CallContext *ctx) QQmlEngine *engine = v8engine->engine(); QQmlContextData *context = v8engine->callingContext(); + Q_ASSERT(context); QQmlContext *effectiveContext = 0; if (context->isPragmaLibraryContext) effectiveContext = engine->rootContext(); else effectiveContext = context->asQQmlContext(); - Q_ASSERT(context && effectiveContext); + Q_ASSERT(effectiveContext); QString qml = ctx->callData->args[0].toQStringNoThrow(); if (qml.isEmpty()) @@ -1086,10 +1087,10 @@ ReturnedValue QtObject::method_createComponent(CallContext *ctx) QQmlEngine *engine = v8engine->engine(); QQmlContextData *context = v8engine->callingContext(); + Q_ASSERT(context); QQmlContextData *effectiveContext = context; if (context->isPragmaLibraryContext) effectiveContext = 0; - Q_ASSERT(context); QString arg = ctx->callData->args[0].toQStringNoThrow(); if (arg.isEmpty()) diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 1b074efd56..a0148715ce 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1694,13 +1694,20 @@ void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &r } } +void QQmlListModel::emitItemsAboutToBeRemoved(int index, int count) +{ + if (count <= 0 || !m_mainThread) + return; + + beginRemoveRows(QModelIndex(), index, index + count - 1); +} + void QQmlListModel::emitItemsRemoved(int index, int count) { if (count <= 0) return; if (m_mainThread) { - beginRemoveRows(QModelIndex(), index, index + count - 1); endRemoveRows(); emit countChanged(); } else { @@ -1711,13 +1718,20 @@ void QQmlListModel::emitItemsRemoved(int index, int count) } } +void QQmlListModel::emitItemsAboutToBeInserted(int index, int count) +{ + if (count <= 0 || !m_mainThread) + return; + + beginInsertRows(QModelIndex(), index, index + count - 1); +} + void QQmlListModel::emitItemsInserted(int index, int count) { if (count <= 0) return; if (m_mainThread) { - beginInsertRows(QModelIndex(), index, index + count - 1); endInsertRows(); emit countChanged(); } else { @@ -1726,13 +1740,20 @@ void QQmlListModel::emitItemsInserted(int index, int count) } } +void QQmlListModel::emitItemsAboutToBeMoved(int from, int to, int n) +{ + if (n <= 0 || !m_mainThread) + return; + + beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); +} + void QQmlListModel::emitItemsMoved(int from, int to, int n) { if (n <= 0) return; if (m_mainThread) { - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); endMoveRows(); } else { int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); @@ -1872,6 +1893,8 @@ void QQmlListModel::clear() { int cleared = count(); + emitItemsAboutToBeRemoved(0, cleared); + if (m_dynamicRoles) { for (int i=0 ; i < m_modelObjects.count() ; ++i) delete m_modelObjects[i]; @@ -1904,6 +1927,8 @@ void QQmlListModel::remove(QQmlV4Function *args) return; } + emitItemsAboutToBeRemoved(index, removeCount); + if (m_dynamicRoles) { for (int i=0 ; i < removeCount ; ++i) delete m_modelObjects[index+i]; @@ -1952,6 +1977,7 @@ void QQmlListModel::insert(QQmlV4Function *args) QV4::ScopedObject argObject(scope); int objectArrayLength = objectArray->getLength(); + emitItemsAboutToBeInserted(index, objectArrayLength); for (int i=0 ; i < objectArrayLength ; ++i) { argObject = objectArray->getIndexed(i); @@ -1963,6 +1989,8 @@ void QQmlListModel::insert(QQmlV4Function *args) } emitItemsInserted(index, objectArrayLength); } else if (argObject) { + emitItemsAboutToBeInserted(index, 1); + if (m_dynamicRoles) { m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); } else { @@ -2001,6 +2029,8 @@ void QQmlListModel::move(int from, int to, int n) return; } + emitItemsAboutToBeMoved(from, to, n); + if (m_dynamicRoles) { int realFrom = from; @@ -2056,6 +2086,8 @@ void QQmlListModel::append(QQmlV4Function *args) int objectArrayLength = objectArray->getLength(); int index = count(); + emitItemsAboutToBeInserted(index, objectArrayLength); + for (int i=0 ; i < objectArrayLength ; ++i) { argObject = objectArray->getIndexed(i); @@ -2072,9 +2104,12 @@ void QQmlListModel::append(QQmlV4Function *args) if (m_dynamicRoles) { index = m_modelObjects.count(); + emitItemsAboutToBeInserted(index, 1); m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); } else { - index = m_listModel->append(argObject, args->engine()); + index = m_listModel->elementCount(); + emitItemsAboutToBeInserted(index, 1); + m_listModel->append(argObject, args->engine()); } emitItemsInserted(index, 1); @@ -2169,6 +2204,7 @@ void QQmlListModel::set(int index, const QQmlV4Handle &handle) if (index == count()) { + emitItemsAboutToBeInserted(index, 1); if (m_dynamicRoles) { m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 59cfce81e5..8c12173425 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -144,8 +144,11 @@ private: static QQmlListModel *createWithOwner(QQmlListModel *newOwner); void emitItemsChanged(int index, int count, const QVector<int> &roles); + void emitItemsAboutToBeRemoved(int index, int count); void emitItemsRemoved(int index, int count); + void emitItemsAboutToBeInserted(int index, int count); void emitItemsInserted(int index, int count); + void emitItemsAboutToBeMoved(int from, int to, int n); void emitItemsMoved(int from, int to, int n); }; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 507e94fb7e..80c4112930 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -382,36 +382,44 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - QFile f(fileName); - if (f.open(QIODevice::ReadOnly)) { - QByteArray data = f.readAll(); - QString sourceCode = QString::fromUtf8(data); - QmlIR::Document::removeScriptPragmas(sourceCode); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine); + QV4::Scope scope(v4); + QScopedPointer<QV4::Script> program; - WorkerScript *script = workers.value(id); - if (!script) - return; - script->source = url; + WorkerScript *script = workers.value(id); + if (!script) + return; + script->source = url; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine); - QV4::Scope scope(v4); + QV4::Scoped<QV4::Object> activation(scope, getWorker(script)); + if (!activation) + return; - QV4::Scoped<QV4::Object> activation(scope, getWorker(script)); - if (!activation) + if (const QQmlPrivate::CachedQmlUnit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(url)) { + QV4::CompiledData::CompilationUnit *jsUnit = cachedUnit->createCompilationUnit(); + program.reset(new QV4::Script(v4, activation, jsUnit)); + } else { + QFile f(fileName); + if (!f.open(QIODevice::ReadOnly)) { + qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); return; + } + + QByteArray data = f.readAll(); + QString sourceCode = QString::fromUtf8(data); + QmlIR::Document::removeScriptPragmas(sourceCode); - QV4::Script program(v4, activation, sourceCode, url.toString()); + program.reset(new QV4::Script(v4, activation, sourceCode, url.toString())); + program->parse(); + } + + if (!v4->hasException) + program->run(); + if (v4->hasException) { QV4::ExecutionContext *ctx = v4->currentContext(); - program.parse(); - if (!v4->hasException) - program.run(); - if (v4->hasException) { - QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx); - reportScriptException(script, error); - } - } else { - qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); + QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx); + reportScriptException(script, error); } } diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 29e83e8247..a7d5f7d65f 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -3177,11 +3177,6 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(QV4::Managed *m, uint QV4::ExecutionEngine *v4 = m->engine(); QV4::Scope scope(v4); QV4::Scoped<QQuickJSContext2DPixelData> r(scope, m->as<QQuickJSContext2DPixelData>()); - if (!m) { - if (hasProperty) - *hasProperty = false; - return m->engine()->currentContext()->throwTypeError(); - } if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4)) { if (hasProperty) @@ -3610,10 +3605,12 @@ void QQuickContext2D::clip() QPainterPath clipPath = m_path; clipPath.closeSubpath(); - if (!state.clipPath.isEmpty()) + if (state.clip) { state.clipPath = clipPath.intersected(state.clipPath); - else + } else { + state.clip = true; state.clipPath = clipPath; + } buffer()->clip(state.clipPath); } @@ -4282,9 +4279,8 @@ void QQuickContext2D::popState() if (newState.miterLimit != state.miterLimit) buffer()->setMiterLimit(newState.miterLimit); - if (newState.clipPath != state.clipPath) { + if (newState.clip && (!state.clip || newState.clipPath != state.clipPath)) buffer()->clip(newState.clipPath); - } if (newState.shadowBlur != state.shadowBlur) buffer()->setShadowBlur(newState.shadowBlur); @@ -4312,12 +4308,6 @@ void QQuickContext2D::reset() m_path = QPainterPath(); - QPainterPath defaultClipPath; - - QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height()); - r = r.united(m_canvas->canvasWindow().toRect()); - defaultClipPath.addRect(r); - newState.clipPath = defaultClipPath; newState.clipPath.setFillRule(Qt::WindingFill); m_stateStack.clear(); diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index ab851d302f..bd1a83ce08 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -123,6 +123,7 @@ public: , strokePatternRepeatX(false) , strokePatternRepeatY(false) , invertibleCTM(true) + , clip(false) , fillRule(Qt::WindingFill) , globalAlpha(1.0) , lineWidth(1) @@ -150,6 +151,7 @@ public: bool strokePatternRepeatX:1; bool strokePatternRepeatY:1; bool invertibleCTM:1; + bool clip:1; Qt::FillRule fillRule; qreal globalAlpha; qreal lineWidth; diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index 5697c25ff0..cb09c9d4ff 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -215,7 +215,8 @@ void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, const QQuickCont if (state.globalCompositeOperation != p->compositionMode()) p->setCompositionMode(state.globalCompositeOperation); - p->setClipPath(state.clipPath); + if (state.clip) + p->setClipPath(state.clipPath); } static void qt_drawImage(QPainter *p, QQuickContext2D::State& state, QImage image, const QRectF& sr, const QRectF& dr, bool shadow = false) diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index b5cc6ea3ef..16f684ce77 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -56,16 +56,39 @@ QT_BEGIN_NAMESPACE \ingroup qtquick-visual-utility \ingroup accessibility - This class is part of \l {Accessibility for Qt Quick Applications}. + This class is part of the \l {Accessibility for Qt Quick Applications}. Items the user interacts with or that give information to the user - need to expose their information in a semantic way. + need to expose their information to the accessibility framework. Then assistive tools can make use of that information to enable users to interact with the application in various ways. - This enables Qt Quick applications to be used with screen-readers for example. - The most important properties to set are \l name and \l role. + The most important properties are \l name, \l description and \l role. + + Example implementation of a simple button: + \qml + Rectangle { + id: myButton + Text { + id: label + text: "next" + } + Accessible.role: Accessible.Button + Accessible.name: label.text + Accessible.description: "shows the next page" + function accessiblePressAction() { + // do a button click + } + } + \endqml + The \l role is set to \c Button to indicate the type of control. + \l Accessible.name is the most important information and bound to the text on the button. + The name is a short and consise description of the control and should reflect the visual label. + In this case it is not clear what the button does with the name only, so \l description contains + an explanation. + There is also a function \c accessiblePressAction() which can be invoked by assistive tools to trigger + the button. This function needs to have the same effect as tapping or clicking the button would have. \sa Accessibility */ @@ -94,25 +117,7 @@ QT_BEGIN_NAMESPACE This flags sets the semantic type of the widget. A button for example would have "Button" as type. - The value must be one of \l QAccessible::Role . - Example: - \qml - Item { - id: myButton - - Text { - id: label - // ... - } - - Accessible.name: label.text - Accessible.role: Accessible.Button - - function accessiblePressAction() { - //... - } - } - \endqml + The value must be one of \l QAccessible::Role. Some roles have special semantics. In order to implement check boxes for example a "checked" property is expected. @@ -120,74 +125,116 @@ QT_BEGIN_NAMESPACE \table \header \li \b {Role} - \li \b {Expected property} - \li - + \li \b {Properties and functions} + \li \b {Explanation} \row - \li Button - \li function accessiblePressAction - \li Called when the button receives a press action. The implementation should visually simulate a button click and perform the button action. + \li All interactive elements + \li \l focusable and \l focused + \li All elements that the user can interact with should have focusable set to \c true and + set \l focus to \c true when they have the focus. This is important even for applications + that run on touch-only devices since screen readers often implement a virtual focus that + can be moved from item to item. \row - \li CheckBox, Radiobutton - \li checked + \li Button, CheckBox, RadioButton + \li \c accessiblePressAction() + \li A button should have a function with the name \c accessiblePressAction. + This function may be called by an assistive tool such as a screen-reader. + The implementation needs to behave the same as a mouse click or tap on the button. + \row + \li CheckBox, RadioButton + \li \l checkable, \l checked \li The check state of the check box. Updated on Press, Check and Uncheck actions. \row \li Slider, SpinBox, Dial, ScrollBar - \li value, minimumValue, maximumValue, stepSize - \li value will be updated on increase and decrase actions, in accordance with the other properties - + \li \c value, \c minimumValue, \c maximumValue, \c stepSize + \li These properties reflect the state and possible values for the elements. + \row + \li Slider, SpinBox, Dial, ScrollBar + \li \c accessibleIncreaseAction(), \c accessibleDecreaseAction() + \li Actions to increase and decrease the value of the element. \endtable */ -/*! \qmlproperty bool focusable +/*! \qmlproperty bool QtQuick::Accessible::focusable \brief This property holds whether this item is focusable. - By default, this property is false except for items where the role is one of - CheckBox, RadioButton, Button, MenuItem, PageTab, EditableText, SpinBox, ComboBox, - Terminal or ScrollBar. + By default, this property is \c false except for items where the role is one of + \c CheckBox, \c RadioButton, \c Button, \c MenuItem, \c PageTab, \c EditableText, \c SpinBox, \c ComboBox, + \c Terminal or \c ScrollBar. + \sa focused */ -/*! \qmlproperty bool focused +/*! \qmlproperty bool QtQuick::Accessible::focused \brief This property holds whether this item currently has the active focus. - By default, this property is false, but it will return true for items that - have \l QQuickItem::hasActiveFocus() returning true. + By default, this property is \c false, but it will return \c true for items that + have \l QQuickItem::hasActiveFocus() returning \c true. + \sa focusable */ -/*! \qmlproperty bool checkable +/*! \qmlproperty bool QtQuick::Accessible::checkable \brief This property holds whether this item is checkable (like a check box or some buttons). + + By default this property is \c false. + \sa checked */ -/*! \qmlproperty bool checked +/*! \qmlproperty bool QtQuick::Accessible::checked \brief This property holds whether this item is currently checked. + + By default this property is \c false. + \sa checkable */ -/*! \qmlproperty bool editable +/*! \qmlproperty bool QtQuick::Accessible::editable \brief This property holds whether this item has editable text. + + By default this property is \c false. */ -/*! \qmlproperty bool multiLine +/*! \qmlproperty bool QtQuick::Accessible::multiLine \brief This property holds whether this item has multiple text lines. + + By default this property is \c false. */ -/*! \qmlproperty bool readOnly - \brief This property holds whether this item while being of type \l QAccessible::EditableText - is set to read-only. +/*! \qmlproperty bool QtQuick::Accessible::readOnly + \brief This property indicates that a text field is read only. + + It is relevant when the role is \l QAccessible::EditableText and set to be read-only. + By default this property is \c false. */ -/*! \qmlproperty bool selected +/*! \qmlproperty bool QtQuick::Accessible::selected \brief This property holds whether this item is selected. + + By default this property is \c false. + \sa selectable */ -/*! \qmlproperty bool selectable +/*! \qmlproperty bool QtQuick::Accessible::selectable \brief This property holds whether this item can be selected. + + By default this property is \c false. + \sa selected */ -/*! \qmlproperty bool pressed +/*! \qmlproperty bool QtQuick::Accessible::pressed \brief This property holds whether this item is pressed (for example a button during a mouse click). + + By default this property is \c false. */ -/*! \qmlproperty bool checkStateMixed +/*! \qmlproperty bool QtQuick::Accessible::checkStateMixed \brief This property holds whether this item is in the partially checked state. + + By default this property is \c false. + \sa checked, checkable */ -/*! \qmlproperty bool defaultButton +/*! \qmlproperty bool QtQuick::Accessible::defaultButton \brief This property holds whether this item is the default button of a dialog. + + By default this property is \c false. */ -/*! \qmlproperty bool passwordEdit +/*! \qmlproperty bool QtQuick::Accessible::passwordEdit \brief This property holds whether this item is a password text edit. + + By default this property is \c false. */ -/*! \qmlproperty bool selectableText +/*! \qmlproperty bool QtQuick::Accessible::selectableText \brief This property holds whether this item contains selectable text. + + By default this property is \c false. */ QQuickAccessibleAttached::QQuickAccessibleAttached(QObject *parent) diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp index 3974073b55..78c29ea4a7 100644 --- a/src/quick/items/qquickclipnode.cpp +++ b/src/quick/items/qquickclipnode.cpp @@ -114,8 +114,8 @@ void QQuickDefaultClipNode::updateGeometry() } } - markDirty(DirtyGeometry); } + markDirty(DirtyGeometry); setClipRect(m_rect); } diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 01d1305260..f2f90935a3 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -900,7 +900,7 @@ bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event) default: break; } - grabber = c->mouseGrabberItem(); + grabber = c ? c->mouseGrabberItem() : 0; if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) grabMouse(); diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index c20559454e..ee31c018af 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -844,7 +844,7 @@ bool QQuickMultiPointTouchArea::sendMouseEvent(QMouseEvent *event) default: break; } - grabber = c->mouseGrabberItem(); + grabber = c ? c->mouseGrabberItem() : 0; if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) grabMouse(); diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 55736c33c3..b8d572114a 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -120,13 +120,13 @@ void QQuickRenderControl::initialize(QOpenGLContext *gl) // surface belonging to window. In fact window may not have a native // window/surface at all. - QQuickWindowPrivate::get(d->window)->context->initialize(gl); + d->rc->initialize(gl); } void QQuickRenderControl::invalidate() { Q_D(QQuickRenderControl); - QQuickWindowPrivate::get(d->window)->context->invalidate(); + d->rc->invalidate(); } /*! @@ -242,6 +242,10 @@ void QQuickRenderControl::setWindow(QQuickWindow *window) d->window = window; } +/*! + Returns the offscreen window. + */ + QQuickWindow *QQuickRenderControl::window() const { Q_D(const QQuickRenderControl); @@ -249,6 +253,21 @@ QQuickWindow *QQuickRenderControl::window() const } /*! + Create an offscreen QQuickWindow for this render control, + unless the render control already has a window(). + + Returns the offscreen window if one is created, otherwise returns null. + The caller takes ownership of the window, and is responsible for deleting it. + */ +QQuickWindow *QQuickRenderControl::createOffscreenWindow() +{ + Q_D(QQuickRenderControl); + if (!d->window) + return new QQuickWindow(this); + return 0; +} + +/*! \fn QWindow *QQuickRenderControl::renderWindow(QPoint *offset) Reimplemented in subclasses to return the real window this render control diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h index e7b7759afa..cc30e37724 100644 --- a/src/quick/items/qquickrendercontrol_p.h +++ b/src/quick/items/qquickrendercontrol_p.h @@ -63,6 +63,7 @@ public: ~QQuickRenderControl(); QQuickWindow *window() const; + QQuickWindow *createOffscreenWindow(); virtual QWindow *renderWindow(QPoint *offset) { Q_UNUSED(offset); return 0; } static QWindow *renderWindowFor(QQuickWindow *win, QPoint *offset = 0); diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index e57b7abddc..8bcd2e64db 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -97,7 +97,8 @@ namespace { // Returns -1 if not found, returns index to first character after the name if found. int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl, int &typeIndex, int &typeLength, - int &nameIndex, int &nameLength) + int &nameIndex, int &nameLength, + QQuickShaderEffectCommon::Key::ShaderType shaderType) { enum Identifier { QualifierIdentifier, // Base state @@ -124,6 +125,7 @@ namespace { int idLength = index - idIndex; const int attrLen = sizeof("attribute") - 1; + const int inLen = sizeof("in") - 1; const int uniLen = sizeof("uniform") - 1; const int loLen = sizeof("lowp") - 1; const int medLen = sizeof("mediump") - 1; @@ -134,6 +136,10 @@ namespace { if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) { decl = AttributeQualifier; expected = PrecisionIdentifier; + } else if (shaderType == QQuickShaderEffectCommon::Key::VertexShader + && idLength == inLen && qstrncmp("in", s + idIndex, inLen) == 0) { + decl = AttributeQualifier; + expected = PrecisionIdentifier; } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) { decl = UniformQualifier; expected = PrecisionIdentifier; @@ -287,7 +293,7 @@ void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::Shad const char *s = code.constData(); VariableQualifier decl = AttributeQualifier; while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength, - nameIndex, nameLength)) != -1) + nameIndex, nameLength, shaderType)) != -1) { if (decl == AttributeQualifier) { if (shaderType == Key::VertexShader) diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 1a4adb0785..c95ec5b46d 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -199,7 +199,7 @@ private Q_SLOTS: private: friend class QQuickItem; friend class QQuickWidget; - friend class QQuickWidgetPrivate; + friend class QQuickRenderControl; friend class QQuickAnimatorController; explicit QQuickWindow(QQuickRenderControl*); Q_DISABLE_COPY(QQuickWindow) diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 99b52a65cb..6a69ea02db 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -416,6 +416,7 @@ QSGRenderContext *QSGRenderContext::from(QOpenGLContext *context) void QSGRenderContext::registerFontengineForCleanup(QFontEngine *engine) { + engine->ref.ref(); m_fontEnginesToClean << engine; } @@ -495,6 +496,8 @@ void QSGRenderContext::invalidate() for (QSet<QFontEngine *>::const_iterator it = m_fontEnginesToClean.constBegin(), end = m_fontEnginesToClean.constEnd(); it != end; ++it) { (*it)->clearGlyphCache(m_gl); + if (!(*it)->ref.deref()) + delete *it; } m_fontEnginesToClean.clear(); diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index a69cbc54ea..c3234be158 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -352,6 +352,7 @@ QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font, QFontEngine::Glyp QSGTextMaskMaterial::~QSGTextMaskMaterial() { + delete m_texture; } void QSGTextMaskMaterial::init(QFontEngine::GlyphFormat glyphFormat) diff --git a/src/quick/util/qquickanimationcontroller.cpp b/src/quick/util/qquickanimationcontroller.cpp index 0214dde597..6a9308da3e 100644 --- a/src/quick/util/qquickanimationcontroller.cpp +++ b/src/quick/util/qquickanimationcontroller.cpp @@ -142,7 +142,7 @@ void QQuickAnimationController::setProgress(qreal progress) } /*! - \qmlproperty real QtQuick::AnimationController::animation + \qmlproperty Animation QtQuick::AnimationController::animation \default This property holds the animation to be controlled by the AnimationController. diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index b90ca47fd1..0a5b26281a 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -810,7 +810,7 @@ void QQuickPixmapStore::unreferencePixmap(QQuickPixmapData *data) if (!m_lastUnreferencedPixmap) m_lastUnreferencedPixmap = data; - shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit + shrinkCache(-1); // Shrink the cache in case it has become larger than cache_limit if (m_timerId == -1 && m_unreferencedPixmaps && !m_destroying) m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000); diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 53176faaa6..a8a33c07c8 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -86,7 +86,7 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) Q_Q(QQuickWidget); renderControl = new QQuickWidgetRenderControl(q); - offscreenWindow = new QQuickWindow(renderControl); + offscreenWindow = renderControl->createOffscreenWindow(); offscreenWindow->setTitle(QString::fromLatin1("Offscreen")); // Do not call create() on offscreenWindow. createOffscreenSurface(); @@ -107,15 +107,33 @@ void QQuickWidgetPrivate::init(QQmlEngine* e) if (QQmlDebugService::isDebuggingEnabled()) QQmlInspectorService::instance()->addView(q); +#ifndef QT_NO_DRAGANDDROP + q->setAcceptDrops(true); +#endif + QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInitialized()), q, SLOT(createFramebufferObject())); QWidget::connect(offscreenWindow, SIGNAL(sceneGraphInvalidated()), q, SLOT(destroyFramebufferObject())); QObject::connect(renderControl, SIGNAL(renderRequested()), q, SLOT(triggerUpdate())); QObject::connect(renderControl, SIGNAL(sceneChanged()), q, SLOT(triggerUpdate())); } -void QQuickWidgetPrivate::handleWindowChange() +void QQuickWidgetPrivate::stopRenderControl() { + if (!context) // this is not an error, could be called before creating the context, or multiple times + return; + + bool success = context->makeCurrent(offscreenSurface); + if (!success) { + qWarning("QQuickWidget::stopRenderControl could not make context current"); + return; + } + renderControl->stop(); +} + +void QQuickWidgetPrivate::handleWindowChange() +{ + stopRenderControl(); destroyContext(); } @@ -139,10 +157,17 @@ QQuickWidgetPrivate::~QQuickWidgetPrivate() { if (QQmlDebugService::isDebuggingEnabled()) QQmlInspectorService::instance()->removeView(q_func()); - delete offscreenSurface; + + stopRenderControl(); + + // context and offscreenSurface are current at this stage, if the context was created. + Q_ASSERT(!context || (QOpenGLContext::currentContext() == context && context->surface() == offscreenSurface)); delete offscreenWindow; delete renderControl; delete fbo; + + delete offscreenSurface; + destroyContext(); } void QQuickWidgetPrivate::createOffscreenSurface() @@ -624,9 +649,6 @@ void QQuickWidgetPrivate::createContext() void QQuickWidgetPrivate::destroyContext() { - if (!context) - return; - renderControl->invalidate(); delete context; context = 0; } @@ -635,8 +657,6 @@ void QQuickWidget::createFramebufferObject() { Q_D(QQuickWidget); - if (d->fbo) - delete d->fbo; QOpenGLContext *context = d->offscreenWindow->openglContext(); if (!context) { @@ -650,6 +670,8 @@ void QQuickWidget::createFramebufferObject() } context->makeCurrent(d->offscreenSurface); + + delete d->fbo; d->fbo = new QOpenGLFramebufferObject(size() * window()->devicePixelRatio()); d->fbo->setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); d->offscreenWindow->setRenderTarget(d->fbo); @@ -902,17 +924,7 @@ void QQuickWidget::showEvent(QShowEvent *) void QQuickWidget::hideEvent(QHideEvent *) { Q_D(QQuickWidget); - - if (!d->context) { - qWarning("QQuickWidget::hideEvent with no context"); - return; - } - bool success = d->context->makeCurrent(d->offscreenSurface); - if (!success) { - qWarning("QQuickWidget::hideEvent could not make context current"); - return; - } - d->renderControl->stop(); + d->stopRenderControl(); } /*! \reimp */ @@ -960,13 +972,22 @@ void QQuickWidget::focusOutEvent(QFocusEvent * event) d->offscreenWindow->focusOutEvent(event); } - /*! \reimp */ bool QQuickWidget::event(QEvent *e) { Q_D(QQuickWidget); switch (e->type()) { +#ifndef QT_NO_DRAGANDDROP + case QEvent::Drop: + case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::DragLeave: + // Drag/drop events only have local pos, so no need to map, + // but QQuickWindow::event() does not return true + d->offscreenWindow->event(e); + return e->isAccepted(); +#endif case QEvent::TouchBegin: case QEvent::TouchEnd: case QEvent::TouchUpdate: diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index 5cf2bfbf1d..8efe32b3ce 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -93,6 +93,7 @@ public: void init(QQmlEngine* e = 0); void handleWindowChange(); + void stopRenderControl(); QSize rootObjectSize() const; diff --git a/tests/auto/qml/debugger/debugger.pro b/tests/auto/qml/debugger/debugger.pro index 4c4342a6a5..303e78db75 100644 --- a/tests/auto/qml/debugger/debugger.pro +++ b/tests/auto/qml/debugger/debugger.pro @@ -6,11 +6,7 @@ PUBLICTESTS += \ qqmlinspector \ qqmlprofilerservice \ qpacketprotocol \ -# qv4profilerservice \ -# qdebugmessageservice \ qqmlenginedebuginspectorintegrationtest \ - qqmlinspector \ - qqmlprofilerservice \ qqmlenginecontrol PRIVATETESTS += \ diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index a918e23a05..5fd985f6d5 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -136,7 +136,9 @@ public: { } - QList<QQmlProfilerData> traceMessages; + QList<QQmlProfilerData> synchronousMessages; + QList<QQmlProfilerData> asynchronousMessages; + QList<QQmlProfilerData> pixmapMessages; void setTraceState(bool enabled) { QByteArray message; @@ -171,6 +173,7 @@ private: QQmlProfilerClient *m_client; void connect(bool block, const QString &testFile); + void checkTraceReceived(); private slots: void cleanup(); @@ -305,7 +308,17 @@ void QQmlProfilerClient::messageReceived(const QByteArray &message) break; } QVERIFY(stream.atEnd()); - traceMessages.append(data); + if (data.messageType == QQmlProfilerClient::PixmapCacheEvent) + pixmapMessages.append(data); + else if (data.messageType == QQmlProfilerClient::SceneGraphFrame || + (data.messageType == QQmlProfilerClient::Event && + (data.detailType == QQmlProfilerClient::FramePaint || + data.detailType == QQmlProfilerClient::AnimationFrame || + data.detailType == QQmlProfilerClient::Mouse || + data.detailType == QQmlProfilerClient::Key))) + asynchronousMessages.append(data); + else + synchronousMessages.append(data); } void tst_QQmlProfilerService::connect(bool block, const QString &testFile) @@ -331,9 +344,44 @@ void tst_QQmlProfilerService::connect(bool block, const QString &testFile) m_connection->connectToHost(QLatin1String("127.0.0.1"), port); } +void tst_QQmlProfilerService::checkTraceReceived() +{ + QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); + QVERIFY(m_client->synchronousMessages.count()); + + // must start with "StartTrace" + QCOMPARE(m_client->synchronousMessages.first().messageType, (int)QQmlProfilerClient::Event); + QCOMPARE(m_client->synchronousMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); + + // must end with "EndTrace" + QCOMPARE(m_client->synchronousMessages.last().messageType, (int)QQmlProfilerClient::Event); + QCOMPARE(m_client->synchronousMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); +} + void tst_QQmlProfilerService::cleanup() { if (QTest::currentTestFailed()) { + qDebug() << "Synchronous Messages:" << m_client->synchronousMessages.count(); + int i = 0; + foreach (const QQmlProfilerData &data, m_client->synchronousMessages) { + qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData + << data.line << data.column; + } + qDebug() << " "; + qDebug() << "Asynchronous Messages:" << m_client->asynchronousMessages.count(); + i = 0; + foreach (const QQmlProfilerData &data, m_client->asynchronousMessages) { + qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData + << data.framerate << data.animationcount << data.line << data.column; + } + qDebug() << " "; + qDebug() << "Pixmap Cache Messages:" << m_client->pixmapMessages.count(); + i = 0; + foreach (const QQmlProfilerData &data, m_client->pixmapMessages) { + qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData + << data.line << data.column; + } + qDebug() << " "; qDebug() << "Process State:" << (m_process ? m_process->state() : QLatin1String("null")); qDebug() << "Application Output:" << (m_process ? m_process->output() : QLatin1String("null")); qDebug() << "Connection State:" << (m_connection ? m_connection->stateString() : QLatin1String("null")); @@ -355,16 +403,7 @@ void tst_QQmlProfilerService::blockingConnectWithTraceEnabled() m_client->setTraceState(true); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - - QVERIFY(m_client->traceMessages.count()); - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + checkTraceReceived(); } void tst_QQmlProfilerService::blockingConnectWithTraceDisabled() @@ -376,17 +415,7 @@ void tst_QQmlProfilerService::blockingConnectWithTraceDisabled() m_client->setTraceState(false); m_client->setTraceState(true); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - - QVERIFY(m_client->traceMessages.count()); - - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + checkTraceReceived(); } void tst_QQmlProfilerService::nonBlockingConnect() @@ -397,16 +426,7 @@ void tst_QQmlProfilerService::nonBlockingConnect() m_client->setTraceState(true); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - QVERIFY(m_client->traceMessages.count()); - - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + checkTraceReceived(); } void tst_QQmlProfilerService::pixmapCacheData() @@ -424,35 +444,27 @@ void tst_QQmlProfilerService::pixmapCacheData() m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - QVERIFY2(m_client->traceMessages.count() >= 20, - QString::number(m_client->traceMessages.count()).toUtf8().constData()); - - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); + checkTraceReceived(); + QVERIFY2(m_client->pixmapMessages.count() >= 4, + QString::number(m_client->pixmapMessages.count()).toUtf8().constData()); // image starting to load - QCOMPARE(m_client->traceMessages[16].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); - QCOMPARE(m_client->traceMessages[16].detailType, (int)QQmlProfilerClient::PixmapLoadingStarted); + QCOMPARE(m_client->pixmapMessages[0].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); + QCOMPARE(m_client->pixmapMessages[0].detailType, (int)QQmlProfilerClient::PixmapLoadingStarted); // image size - QCOMPARE(m_client->traceMessages[17].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); - QCOMPARE(m_client->traceMessages[17].detailType, (int)QQmlProfilerClient::PixmapSizeKnown); - QCOMPARE(m_client->traceMessages[17].line, 2); // width - QCOMPARE(m_client->traceMessages[17].column, 2); // height + QCOMPARE(m_client->pixmapMessages[1].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); + QCOMPARE(m_client->pixmapMessages[1].detailType, (int)QQmlProfilerClient::PixmapSizeKnown); + QCOMPARE(m_client->pixmapMessages[1].line, 2); // width + QCOMPARE(m_client->pixmapMessages[1].column, 2); // height // image loaded - QCOMPARE(m_client->traceMessages[18].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); - QCOMPARE(m_client->traceMessages[18].detailType, (int)QQmlProfilerClient::PixmapLoadingFinished); + QCOMPARE(m_client->pixmapMessages[2].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); + QCOMPARE(m_client->pixmapMessages[2].detailType, (int)QQmlProfilerClient::PixmapLoadingFinished); // cache size - QCOMPARE(m_client->traceMessages[19].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); - QCOMPARE(m_client->traceMessages[19].detailType, (int)QQmlProfilerClient::PixmapCacheCountChanged); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + QCOMPARE(m_client->pixmapMessages[3].messageType, (int)QQmlProfilerClient::PixmapCacheEvent); + QCOMPARE(m_client->pixmapMessages[3].detailType, (int)QQmlProfilerClient::PixmapCacheCountChanged); } @@ -468,8 +480,7 @@ void tst_QQmlProfilerService::scenegraphData() QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - QVERIFY(m_client->traceMessages.count()); + checkTraceReceived(); // check that at least one frame was rendered // there should be a SGPolishAndSync + SGRendererFrame + SGRenderLoopFrame sequence @@ -477,7 +488,7 @@ void tst_QQmlProfilerService::scenegraphData() // // since the rendering happens in a different thread, there could be other unrelated events interleaved int loopcheck = 0; - foreach (const QQmlProfilerData &msg, m_client->traceMessages) { + foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { if (msg.messageType == QQmlProfilerClient::SceneGraphFrame) { if (loopcheck == 0 && msg.detailType == QQmlProfilerClient::SceneGraphContextFrame) loopcheck = 1; @@ -498,17 +509,7 @@ void tst_QQmlProfilerService::profileOnExit() m_client->setTraceState(true); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - QVERIFY2(m_client->traceMessages.count() >= 2, - QString::number(m_client->traceMessages.count()).toUtf8().constData()); - - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + checkTraceReceived(); } void tst_QQmlProfilerService::controlFromJS() @@ -518,17 +519,7 @@ void tst_QQmlProfilerService::controlFromJS() QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - QVERIFY2(m_client->traceMessages.count() >= 2, - QString::number(m_client->traceMessages.count()).toUtf8().constData()); - - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + checkTraceReceived(); } void tst_QQmlProfilerService::signalSourceLocation() @@ -541,32 +532,24 @@ void tst_QQmlProfilerService::signalSourceLocation() while (!(m_process->output().contains(QLatin1String("500")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); - - QVERIFY2(m_client->traceMessages.count() >= 20, - QString::number(m_client->traceMessages.count()).toUtf8().constData()); - - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - QCOMPARE(m_client->traceMessages[14].messageType, (int)QQmlProfilerClient::RangeLocation); - QCOMPARE(m_client->traceMessages[14].detailType, (int)QQmlProfilerClient::HandlingSignal); - QVERIFY2(m_client->traceMessages[14].detailData.endsWith("signalSourceLocation.qml"), - m_client->traceMessages[14].detailData.toUtf8().constData()); - QCOMPARE(m_client->traceMessages[14].line, 8); - QCOMPARE(m_client->traceMessages[14].column, 28); - - QCOMPARE(m_client->traceMessages[19].messageType, (int)QQmlProfilerClient::RangeLocation); - QCOMPARE(m_client->traceMessages[19].detailType, (int)QQmlProfilerClient::HandlingSignal); - QVERIFY2(m_client->traceMessages[19].detailData.endsWith("signalSourceLocation.qml"), - m_client->traceMessages[19].detailData.toUtf8().constData()); - QCOMPARE(m_client->traceMessages[19].line, 7); - QCOMPARE(m_client->traceMessages[19].column, 21); - - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + checkTraceReceived(); + + QVERIFY2(m_client->synchronousMessages.count() >= 20, + QString::number(m_client->synchronousMessages.count()).toUtf8().constData()); + + QCOMPARE(m_client->synchronousMessages[14].messageType, (int)QQmlProfilerClient::RangeLocation); + QCOMPARE(m_client->synchronousMessages[14].detailType, (int)QQmlProfilerClient::HandlingSignal); + QVERIFY2(m_client->synchronousMessages[14].detailData.endsWith("signalSourceLocation.qml"), + m_client->synchronousMessages[14].detailData.toUtf8().constData()); + QCOMPARE(m_client->synchronousMessages[14].line, 8); + QCOMPARE(m_client->synchronousMessages[14].column, 28); + + QCOMPARE(m_client->synchronousMessages[19].messageType, (int)QQmlProfilerClient::RangeLocation); + QCOMPARE(m_client->synchronousMessages[19].detailType, (int)QQmlProfilerClient::HandlingSignal); + QVERIFY2(m_client->synchronousMessages[19].detailData.endsWith("signalSourceLocation.qml"), + m_client->synchronousMessages[19].detailData.toUtf8().constData()); + QCOMPARE(m_client->synchronousMessages[19].line, 7); + QCOMPARE(m_client->synchronousMessages[19].column, 21); } void tst_QQmlProfilerService::javascript() @@ -579,35 +562,28 @@ void tst_QQmlProfilerService::javascript() while (!(m_process->output().contains(QLatin1String("done")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); m_client->setTraceState(false); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); + checkTraceReceived(); - QVERIFY2(m_client->traceMessages.count() >= 36, - QString::number(m_client->traceMessages.count()).toUtf8().constData()); - // must start with "StartTrace" - QCOMPARE(m_client->traceMessages.first().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.first().detailType, (int)QQmlProfilerClient::StartTrace); - - QCOMPARE(m_client->traceMessages[32].messageType, (int)QQmlProfilerClient::RangeStart); - QCOMPARE(m_client->traceMessages[32].detailType, (int)QQmlProfilerClient::Javascript); + QVERIFY2(m_client->synchronousMessages.count() >= 36, + QString::number(m_client->synchronousMessages.count()).toUtf8().constData()); - QCOMPARE(m_client->traceMessages[33].messageType, (int)QQmlProfilerClient::RangeLocation); - QCOMPARE(m_client->traceMessages[33].detailType, (int)QQmlProfilerClient::Javascript); - QVERIFY2(m_client->traceMessages[33].detailData.endsWith("javascript.qml"), - m_client->traceMessages[33].detailData.toUtf8().constData()); - QCOMPARE(m_client->traceMessages[33].line, 4); - QCOMPARE(m_client->traceMessages[33].column, 5); + QCOMPARE(m_client->synchronousMessages[32].messageType, (int)QQmlProfilerClient::RangeStart); + QCOMPARE(m_client->synchronousMessages[32].detailType, (int)QQmlProfilerClient::Javascript); - QCOMPARE(m_client->traceMessages[34].messageType, (int)QQmlProfilerClient::RangeData); - QCOMPARE(m_client->traceMessages[34].detailType, (int)QQmlProfilerClient::Javascript); - QVERIFY2(m_client->traceMessages[34].detailData == "something", - m_client->traceMessages[34].detailData.toUtf8().constData()); + QCOMPARE(m_client->synchronousMessages[33].messageType, (int)QQmlProfilerClient::RangeLocation); + QCOMPARE(m_client->synchronousMessages[33].detailType, (int)QQmlProfilerClient::Javascript); + QVERIFY2(m_client->synchronousMessages[33].detailData.endsWith("javascript.qml"), + m_client->synchronousMessages[33].detailData.toUtf8().constData()); + QCOMPARE(m_client->synchronousMessages[33].line, 4); + QCOMPARE(m_client->synchronousMessages[33].column, 5); - QCOMPARE(m_client->traceMessages[35].messageType, (int)QQmlProfilerClient::RangeEnd); - QCOMPARE(m_client->traceMessages[35].detailType, (int)QQmlProfilerClient::Javascript); + QCOMPARE(m_client->synchronousMessages[34].messageType, (int)QQmlProfilerClient::RangeData); + QCOMPARE(m_client->synchronousMessages[34].detailType, (int)QQmlProfilerClient::Javascript); + QVERIFY2(m_client->synchronousMessages[34].detailData == "something", + m_client->synchronousMessages[34].detailData.toUtf8().constData()); - // must end with "EndTrace" - QCOMPARE(m_client->traceMessages.last().messageType, (int)QQmlProfilerClient::Event); - QCOMPARE(m_client->traceMessages.last().detailType, (int)QQmlProfilerClient::EndTrace); + QCOMPARE(m_client->synchronousMessages[35].messageType, (int)QQmlProfilerClient::RangeEnd); + QCOMPARE(m_client->synchronousMessages[35].detailType, (int)QQmlProfilerClient::Javascript); } QTEST_MAIN(tst_QQmlProfilerService) diff --git a/tests/auto/qml/qqmllanguage/data/idPropertyMismatch.qml b/tests/auto/qml/qqmllanguage/data/idPropertyMismatch.qml new file mode 100644 index 0000000000..8c4fd65786 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/idPropertyMismatch.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +QtObject { + property int id; + id: "root" +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index be417df325..e77c15b79a 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -146,6 +146,7 @@ private slots: void onCompleted(); void onDestruction(); void scriptString(); + void scriptStringWithoutSourceCode(); void defaultPropertyListOrder(); void declaredPropertyValues(); void dontDoubleCallClassBegin(); @@ -1200,21 +1201,32 @@ void tst_qqmllanguage::inlineQmlComponents() // Tests that types that have an id property have it set void tst_qqmllanguage::idProperty() { - QQmlComponent component(&engine, testFileUrl("idProperty.qml")); - VERIFY_ERRORS(0); - MyContainer *object = qobject_cast<MyContainer *>(component.create()); - QVERIFY(object != 0); - QCOMPARE(object->getChildren()->count(), 2); - MyTypeObject *child = - qobject_cast<MyTypeObject *>(object->getChildren()->at(0)); - QVERIFY(child != 0); - QCOMPARE(child->id(), QString("myObjectId")); - QCOMPARE(object->property("object"), QVariant::fromValue((QObject *)child)); + { + QQmlComponent component(&engine, testFileUrl("idProperty.qml")); + VERIFY_ERRORS(0); + MyContainer *object = qobject_cast<MyContainer *>(component.create()); + QVERIFY(object != 0); + QCOMPARE(object->getChildren()->count(), 2); + MyTypeObject *child = + qobject_cast<MyTypeObject *>(object->getChildren()->at(0)); + QVERIFY(child != 0); + QCOMPARE(child->id(), QString("myObjectId")); + QCOMPARE(object->property("object"), QVariant::fromValue((QObject *)child)); - child = - qobject_cast<MyTypeObject *>(object->getChildren()->at(1)); - QVERIFY(child != 0); - QCOMPARE(child->id(), QString("name.with.dots")); + child = + qobject_cast<MyTypeObject *>(object->getChildren()->at(1)); + QVERIFY(child != 0); + QCOMPARE(child->id(), QString("name.with.dots")); + } + { + QQmlComponent component(&engine, testFileUrl("idPropertyMismatch.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> root(component.create()); + QVERIFY(!root.isNull()); + QQmlContext *ctx = qmlContext(root.data()); + QVERIFY(ctx); + QCOMPARE(ctx->nameForObject(root.data()), QString("root")); + } } // Tests automatic connection to notify signals if "onBlahChanged" syntax is used @@ -1932,6 +1944,54 @@ void tst_qqmllanguage::scriptString() } } +void tst_qqmllanguage::scriptStringWithoutSourceCode() +{ + QUrl url = testFileUrl("scriptString7.qml"); + { + QQmlEnginePrivate *eng = QQmlEnginePrivate::get(&engine); + QQmlTypeData *td = eng->typeLoader.getType(url); + Q_ASSERT(td); + + QV4::CompiledData::QmlUnit *qmlUnit = td->compiledData()->qmlUnit; + Q_ASSERT(qmlUnit); + const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); + QCOMPARE(qmlUnit->header.stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); + quint32 i; + for (i = 0; i < rootObject->nBindings; ++i) { + const QV4::CompiledData::Binding *binding = rootObject->bindingTable() + i; + if (qmlUnit->header.stringAt(binding->propertyNameIndex) != QString("scriptProperty")) + continue; + QCOMPARE(binding->valueAsScriptString(&qmlUnit->header), QString("intProperty")); + const_cast<QV4::CompiledData::Binding*>(binding)->stringIndex = 0; // empty string index + QVERIFY(binding->valueAsScriptString(&qmlUnit->header).isEmpty()); + break; + } + QVERIFY(i < rootObject->nBindings); + } + QQmlComponent component(&engine, url); + VERIFY_ERRORS(0); + + MyTypeObject *object = qobject_cast<MyTypeObject*>(component.create()); + QVERIFY(object != 0); + QQmlScriptString ss = object->scriptProperty(); + QVERIFY(!ss.isEmpty()); + QCOMPARE(ss.stringLiteral(), QString()); + bool ok; + QCOMPARE(ss.numberLiteral(&ok), qreal(0.)); + QCOMPARE(ok, false); + + const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(ss); + QVERIFY(scriptPrivate != 0); + QVERIFY(scriptPrivate->script.isEmpty()); + QCOMPARE(scriptPrivate->scope, qobject_cast<QObject*>(object)); + QCOMPARE(scriptPrivate->context, qmlContext(object)); + + { + QQmlExpression expr(ss, /*context*/0, object); + QCOMPARE(expr.evaluate().toInt(), int(100)); + } +} + // Check that default property assignments are correctly spliced into explicit // property assignments void tst_qqmllanguage::defaultPropertyListOrder() diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 22404aa862..ede80b355a 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -132,6 +132,7 @@ private slots: void empty_element_warning_data(); void datetime(); void datetime_data(); + void about_to_be_signals(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -1306,6 +1307,134 @@ void tst_qqmllistmodel::datetime() QVERIFY(expected == dtResult); } +class RowTester : public QObject +{ + Q_OBJECT +public: + RowTester(QAbstractItemModel *model) : QObject(model), model(model) + { + reset(); + connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted())); + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted())); + connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved())); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved())); + connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsAboutToBeMoved())); + connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsMoved())); + } + + void reset() + { + rowsAboutToBeInsertedCalls = 0; + rowsAboutToBeInsertedCount = 0; + rowsInsertedCalls = 0; + rowsInsertedCount = 0; + rowsAboutToBeRemovedCalls = 0; + rowsAboutToBeRemovedCount = 0; + rowsRemovedCalls = 0; + rowsRemovedCount = 0; + rowsAboutToBeMovedCalls = 0; + rowsAboutToBeMovedData.clear(); + rowsMovedCalls = 0; + rowsMovedData.clear(); + } + + int rowsAboutToBeInsertedCalls; + int rowsAboutToBeInsertedCount; + int rowsInsertedCalls; + int rowsInsertedCount; + int rowsAboutToBeRemovedCalls; + int rowsAboutToBeRemovedCount; + int rowsRemovedCalls; + int rowsRemovedCount; + int rowsAboutToBeMovedCalls; + QVariantList rowsAboutToBeMovedData; + int rowsMovedCalls; + QVariantList rowsMovedData; + +private slots: + void rowsAboutToBeInserted() + { + rowsAboutToBeInsertedCalls++; + rowsAboutToBeInsertedCount = model->rowCount(); + } + + void rowsInserted() + { + rowsInsertedCalls++; + rowsInsertedCount = model->rowCount(); + } + + void rowsAboutToBeRemoved() + { + rowsAboutToBeRemovedCalls++; + rowsAboutToBeRemovedCount = model->rowCount(); + } + + void rowsRemoved() + { + rowsRemovedCalls++; + rowsRemovedCount = model->rowCount(); + } + + void rowsAboutToBeMoved() + { + rowsAboutToBeMovedCalls++; + for (int i = 0; i < model->rowCount(); ++i) + rowsAboutToBeMovedData += model->data(model->index(i, 0), 0); + } + + void rowsMoved() + { + rowsMovedCalls++; + for (int i = 0; i < model->rowCount(); ++i) + rowsMovedData += model->data(model->index(i, 0), 0); + } + +private: + QAbstractItemModel *model; +}; + +void tst_qqmllistmodel::about_to_be_signals() +{ + QQmlEngine engine; + QQmlListModel model; + QQmlEngine::setContextForObject(&model,engine.rootContext()); + + RowTester tester(&model); + + QQmlExpression e1(engine.rootContext(), &model, "{append({'test':0})}"); + e1.evaluate(); + + QCOMPARE(tester.rowsAboutToBeInsertedCalls, 1); + QCOMPARE(tester.rowsAboutToBeInsertedCount, 0); + QCOMPARE(tester.rowsInsertedCalls, 1); + QCOMPARE(tester.rowsInsertedCount, 1); + + QQmlExpression e2(engine.rootContext(), &model, "{append({'test':1})}"); + e2.evaluate(); + + QCOMPARE(tester.rowsAboutToBeInsertedCalls, 2); + QCOMPARE(tester.rowsAboutToBeInsertedCount, 1); + QCOMPARE(tester.rowsInsertedCalls, 2); + QCOMPARE(tester.rowsInsertedCount, 2); + + QQmlExpression e3(engine.rootContext(), &model, "{move(0, 1, 1)}"); + e3.evaluate(); + + QCOMPARE(tester.rowsAboutToBeMovedCalls, 1); + QCOMPARE(tester.rowsAboutToBeMovedData, QVariantList() << 0.0 << 1.0); + QCOMPARE(tester.rowsMovedCalls, 1); + QCOMPARE(tester.rowsMovedData, QVariantList() << 1.0 << 0.0); + + QQmlExpression e4(engine.rootContext(), &model, "{remove(0, 2)}"); + e4.evaluate(); + + QCOMPARE(tester.rowsAboutToBeRemovedCalls, 1); + QCOMPARE(tester.rowsAboutToBeRemovedCount, 2); + QCOMPARE(tester.rowsRemovedCalls, 1); + QCOMPARE(tester.rowsRemovedCount, 0); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" diff --git a/tests/auto/qmltest/textinput/tst_textinput.qml b/tests/auto/qmltest/textinput/tst_textinput.qml index c359d53200..9a00ac8a60 100644 --- a/tests/auto/qmltest/textinput/tst_textinput.qml +++ b/tests/auto/qmltest/textinput/tst_textinput.qml @@ -75,6 +75,13 @@ Item { TextInput { id: txtfunctions + text: "The quick brown fox" + height: 20 + width: 50 + } + + TextInput { + id: txtclipboard text: "The quick brown fox jumped over the lazy dog" height: 20 width: 50 @@ -143,31 +150,51 @@ Item { compare(txtentry.text, "hello world") } - function test_functions() { + function test_select_insert() { compare(txtfunctions.getText(4,9), "quick") txtfunctions.select(4,9); compare(txtfunctions.selectedText, "quick") + txtfunctions.insert(4, "very ") + compare(txtfunctions.text, "The very quick brown fox") txtfunctions.deselect(); compare(txtfunctions.selectedText, "") - txtfunctions.select(4,9); - txtfunctions.cut(); - compare(txtfunctions.text, "The brown fox jumped over the lazy dog") txtfunctions.text = "Qt"; txtfunctions.insert(txtfunctions.text.length, " ") compare(txtfunctions.text, "Qt "); - txtfunctions.cursorPosition = txtfunctions.text.length; - txtfunctions.paste(); + txtfunctions.insert(txtfunctions.text.length, "quick") compare(txtfunctions.text, "Qt quick"); txtfunctions.cursorPosition = txtfunctions.text.length; txtfunctions.selectWord(); compare(txtfunctions.selectedText, "quick") - txtfunctions.copy(); txtfunctions.selectAll(); compare(txtfunctions.selectedText, "Qt quick") txtfunctions.deselect(); compare(txtfunctions.selectedText, "") - txtfunctions.paste(); - compare(txtfunctions.text, "Qt quickquick"); + } + + function test_clipboard() { + if (typeof(txtclipboard.copy) !== "function" + || typeof(txtclipboard.paste) !== "function" + || typeof(txtclipboard.cut) !== "function") { + skip("Clipboard is not supported on this platform.") + } + txtclipboard.select(4,10); + txtclipboard.cut(); + compare(txtclipboard.text, "The brown fox jumped over the lazy dog") + txtclipboard.select(30,35) + txtclipboard.paste(); + compare(txtclipboard.text, "The brown fox jumped over the quick dog") + txtclipboard.text = "Qt "; + txtclipboard.cursorPosition = txtclipboard.text.length; + txtclipboard.paste(); + compare(txtclipboard.text, "Qt quick "); + txtclipboard.cursorPosition = txtclipboard.text.length-1; + txtclipboard.selectWord(); + compare(txtclipboard.selectedText, "quick") + txtclipboard.copy(); + txtclipboard.cursorPosition = txtclipboard.text.length; + txtclipboard.paste(); + compare(txtclipboard.text, "Qt quick quick"); } function test_intvalidators_data() { diff --git a/tests/auto/quick/qquicktext/data/horizontalAlignment_RightToLeft.qml b/tests/auto/quick/qquicktext/data/horizontalAlignment_RightToLeft.qml index 5ba4d35684..876d325d5a 100644 --- a/tests/auto/quick/qquicktext/data/horizontalAlignment_RightToLeft.qml +++ b/tests/auto/quick/qquicktext/data/horizontalAlignment_RightToLeft.qml @@ -9,7 +9,7 @@ Rectangle { Rectangle { anchors.centerIn: parent - width: 180 + width: parent.width height: 20 color: "green" diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 9ca7cafe3d..6167577b60 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -732,10 +732,15 @@ void tst_qquicktext::horizontalAlignment() void tst_qquicktext::horizontalAlignment_RightToLeft() { +#if defined(Q_OS_BLACKBERRY) + QQuickWindow dummy; // On BlackBerry first window is always full screen, + dummy.showFullScreen(); // so make test window a second window. +#endif + QQuickView *window = createView(testFile("horizontalAlignment_RightToLeft.qml")); QQuickText *text = window->rootObject()->findChild<QQuickText*>("text"); QVERIFY(text != 0); - window->show(); + window->showNormal(); QQuickTextPrivate *textPrivate = QQuickTextPrivate::get(text); QVERIFY(textPrivate != 0); diff --git a/tools/tools.pro b/tools/tools.pro index a1b48789b8..fffa4df5c0 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -12,7 +12,7 @@ qmlimportscanner.CONFIG = host_build qmlprofiler \ qmlbundle qtHaveModule(quick) { - SUBDIRS += qmlscene qmlplugindump + !static: SUBDIRS += qmlscene qmlplugindump qtHaveModule(widgets): SUBDIRS += qmleasing } qtHaveModule(qmltest): SUBDIRS += qmltestrunner |