From 554fabbcd8888c3f4146ac6f8da2dd7a6d4656b2 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Tue, 1 Sep 2009 13:53:32 +1000 Subject: Rest of 44ab46a6c5dcfb14395baf173a11179839003c4c --- doc/src/declarative/extending.qdoc | 2 +- doc/src/declarative/modules.qdoc | 160 +++++++--------- src/declarative/fx/qfxwebview.h | 1 - src/declarative/qml/qmlcompositetypemanager.cpp | 19 +- src/declarative/qml/qmlengine.cpp | 213 +++++++++------------ .../qmlparser/lib/com/nokia/installedtest/qmldir | 1 + tests/auto/declarative/qmlparser/tst_qmlparser.cpp | 63 ++++++ 7 files changed, 242 insertions(+), 217 deletions(-) diff --git a/doc/src/declarative/extending.qdoc b/doc/src/declarative/extending.qdoc index 833a5db..3235435 100644 --- a/doc/src/declarative/extending.qdoc +++ b/doc/src/declarative/extending.qdoc @@ -818,7 +818,7 @@ Rectangle { \endcode \endtable -Components may be collected into \l {Modules of Components} that gives the +Components may be collected into \l {Modules} that gives the developer more freedom than just putting files in the same directory. \section2 Building reusable components diff --git a/doc/src/declarative/modules.qdoc b/doc/src/declarative/modules.qdoc index 60cf2a4..7c67f60 100644 --- a/doc/src/declarative/modules.qdoc +++ b/doc/src/declarative/modules.qdoc @@ -1,123 +1,105 @@ /*! -\page qmlmodules.html \target qmlmodules -\title Modules of Components - -A \bold module is a collection of \l Components. - -To use a module, include the following statement at the begining -of your QML: - -\code -import "path" -\endcode - -This allows all components defined in the directory \c path to be used in -the component where this statement appears. - -Currently, \c path may only be a directory relative to the directory containing -the component issuing the import. - -The import statement cannot be used by remote content. -*/ - -/* - -Ideas for full module support.... - -See QT-558. - -* Modularity within applications - -This is the currently-supported mechanism. +\page qmlmodules.html +\title Modules -By using the "import" statement, a subdirectory of types can be added to the -empty namespace. Alternatively, a type in a subdirectory can be referenced -explicitly. +A \bold module is a collection of QML types. -So, given these files: +To use types from a module it must be imported using the \c import statement. Successive +import statements override earlier import statements. - ./SubModule1/Type1.qml - ./SubModule2/Type1.qml +\section1 Importing Built-in Types -This is valid QML: +To use built-in types, you must import the module defining them. +For example, to use types from Qt, import it: - import "SubModule1" - Type1 { ... } - SubModule2.Type1 { ... } +\code +import Qt 4.6 +\endcode +This makes available all types in Qt that were available in Qt 4.6, regardless of the +actual version of Qt executing the QML. -* System-installed modules (dependencies) +Modules can be compiled-in (such as the Qt module), or they can be +defined in QML files. -To use system-installed modules, the dependency must be explicitly stated -using the "require" statement. Types in required modules must still be -explicitly qualified. Dependencies cannot be added to the empty namespace. +\section1 Importing QML Files - QMLPATH=/opt/Nokia/qml:/usr/share/lib/qml - /opt/Nokia/qml/Module1/Type1.qml - /usr/share/lib/qml/Module1/Type1.qml +To import types defined in QML files in directories relative to the file importing them, +a quoted import directory is used: - require "Module1" - Module1.Type1 { ... } +\code +import "path" +\endcode +This allows all components defined in the directory \c path to be used in +the component where this statement appears. -* Grouping of components within application modules +To import types defined in QML files that are installed somewhere on the system, +an unquoted URI is used: -Sub-sub directories allow further grouping of types. +\code +import com.nokia.CoolStuff 1.0 +\endcode - ./SubModule1/Group1/*.qml - ./SubModule1/Group2/*.qml +This will access file in the directory \c com/nokia/CoolStuff/, found in some +location determined outside QML. See QmlEngine::addImportPath() and the \c -L option +to the \l {qmlviewer}{viewer} application. - SubModule1.Group1.Type1 { ... } - SubModule1.Group1.Type2 { ... } - SubModule1.Group2.Type1 { ... } - SubModule1.Group2.Type2 { ... } +The directory of installed files must include a file \c qmldir which specifies the +mapping from all type names to versioned QML files. It is a list of lines of the form: - import "SubModule1/Group1" - Type1 { ... } +\code +# + +\endcode - import "SubModule1" - Group1.Type1 { ... } + is the type being made available; is either a single version +number like \c 4.0 or a range of minor versions like \c 4.0-2; is the (relative) +file name of the QML file defining the type. +The same type can be provided by different files in different versions. +If a type is in multiple major versions, it should be listed on a separate line. +Installed files do not need to import the module of which they are a part, as they can refer +to the other QML files in the module as relative (local) files. -* Grouping of components within system-installed modules +Installed files \e must be referred to by version information described above, +local files \e may have it. -System-installed types may also be grouped into types. The hierarchy is a -global namespace, so such grouping is recommended to reduce clashes. +The versioning system ensures that a given QML file will work regardless of the version +of installed software, since a versioned import \e only imports types for that version, +leaving other identifiers available, even if the actual installed version might otherwise +use those identifiers. - /opt/Nokia/qml/Module1/Group1/*.qml - /opt/Nokia/qml/Module1/Group2/*.qml +\section1 Namespaces - Named Imports - require "Module1" - Module1.Group1.Type1 { ... } - Module1.Group1.Type2 { ... } - Module1.Group2.Type1 { ... } - Module1.Group2.Type2 { ... } +When importing content it by default imports types into the global namespace. +You may choose to import the module into another namespace, either to allow identically-named +types to be referenced, or purely for readability. - require "Module1/Group1" - Group1.Type1 { ... } +To import a module into a namespace: - // Alternative syntax - /opt/qml/com/nokia/qml/Module1/Group1/*.qml - require "com.nokia.qml.Module1.Group1" - Group1.Type1 { ... } +\code +import Qt 4.6 as TheQtLibrary +\endcode +Types from Qt 4.6 may then be used, but only by qualifying them with the namespace: -* Private sub-components +\code +TheQtLibrary.Rectangle { ... } +\endcode -Directories begining with _ cannot be referenced except by types in the -directory immediately containing it. +Multiple modules can be imported into the same namespace in the same way that multiple +modules can be imported into the global namespace: - /opt/Nokia/qml/Module1/_private/Type1.qml - ./SubModule1/_private/Type1.qml +\code +import Qt 4.6 as Nokia +import Ovi 1.0 as Nokia +\endcode +*/ - SubModule1._private.Type1 { ... } // Not allowed - import "SubModule1._private" // Not allowed - require "SubModule1._private" // Not allowed - require "SubModule1" - Module1._private.Type1 { ... } // Not allowed +/* - import "_private" // allowed - Type1 { ... } +See original requirement QT-558. */ diff --git a/src/declarative/fx/qfxwebview.h b/src/declarative/fx/qfxwebview.h index 3dd1b65..f136e2d 100644 --- a/src/declarative/fx/qfxwebview.h +++ b/src/declarative/fx/qfxwebview.h @@ -80,7 +80,6 @@ class QFxWebViewAttached; class QFxWebSettings; //### TODO: browser plugins -//### TODO: smart zooming using e.g. DIV class Q_DECLARATIVE_EXPORT QFxWebView : public QFxPaintedItem { diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp index 1b1f67c..f64547c 100644 --- a/src/declarative/qml/qmlcompositetypemanager.cpp +++ b/src/declarative/qml/qmlcompositetypemanager.cpp @@ -329,10 +329,27 @@ void QmlCompositeTypeManager::compile(QmlCompositeTypeData *unit) QUrl url; int majorVersion; int minorVersion; - if (!QmlEnginePrivate::get(engine)->resolveType(unit->imports, typeName, &ref.type, &url, &majorVersion, &minorVersion, 0)) { + QmlEnginePrivate::ImportedNamespace *typeNamespace = 0; + if (!QmlEnginePrivate::get(engine)->resolveType(unit->imports, typeName, &ref.type, &url, &majorVersion, &minorVersion, &typeNamespace)) { // XXX could produce error message here. } + if (typeNamespace) { + QmlError error; + error.setUrl(unit->imports.baseUrl()); + error.setDescription(tr("Namespace %1 cannot be used as a type").arg(QLatin1String(typeName))); + if (!parserRef->refObjects.isEmpty()) { + QmlParser::Object *obj = parserRef->refObjects.first(); + error.setLine(obj->location.start.line); + error.setColumn(obj->location.start.column); + } + unit->status = QmlCompositeTypeData::Error; + unit->errorType = QmlCompositeTypeData::GeneralError; + unit->errors << error; + doComplete(unit); + return; + } + if (ref.type) { foreach (QmlParser::Object *obj, parserRef->refObjects) { // store namespace for DOM diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index d520d18..f650609 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -1138,65 +1138,71 @@ struct QmlEnginePrivate::ImportedNamespace { QList majversions; QList minversions; QList isLibrary; + QList isBuiltin; - QUrl find(const QString& type) const + bool find(const QByteArray& type, int *vmajor, int *vminor, QmlType** type_return, QUrl* url_return) const { for (int i=0; i=0 ? line.indexOf(QLatin1Char(' '),space1+1) : -1; - QStringRef maptype = line.leftRef(space1); - if (maptype==type) { - // eg. 1.2-5 - QString mapversions = line.mid(space1+1,space2<0?line.length()-space1-2:space2-space1-1); - int dot = mapversions.indexOf(QLatin1Char('.')); - int dash = mapversions.indexOf(QLatin1Char('-')); - int mapvmaj = mapversions.left(dot).toInt(); - if (mapvmaj==vmaj) { - int mapvmin_from = (dash <= 0 ? mapversions.mid(dot+1) : mapversions.mid(dot+1,dash-dot-1)).toInt(); - int mapvmin_to = dash <= 0 ? mapvmin_from : mapversions.mid(dash+1).toInt(); - if (vmin >= mapvmin_from && vmin <= mapvmin_to) { - QStringRef mapfile = space2<0 ? QStringRef() : line.midRef(space2+1,line.length()-space2-2); - return url.resolved(mapfile.toString()); + + if (isBuiltin.at(i)) { + QByteArray qt = urls.at(i).toLatin1(); + qt += "/"; + qt += type; + QmlType *t = QmlMetaType::qmlType(qt,vmaj,vmin); + if (vmajor) *vmajor = vmaj; + if (vminor) *vminor = vmin; + if (t) { + if (type_return) + *type_return = t; + return true; + } + } else { + QUrl url = QUrl(urls.at(i) + QLatin1String("/" + type + ".qml")); + if (vmaj || vmin) { + // Check version file - XXX cache these in QmlEngine! + QFile qmldir(QUrl(urls.at(i)+QLatin1String("/qmldir")).toLocalFile()); + if (qmldir.open(QIODevice::ReadOnly)) { + do { + QByteArray lineba = qmldir.readLine(); + if (lineba.at(0) == '#') + continue; + int space1 = lineba.indexOf(' '); + if (qstrncmp(lineba,type,space1)==0) { + // eg. 1.2-5 + QString line = QString::fromUtf8(lineba); + space1 = line.indexOf(QLatin1Char(' ')); // refind in Unicode + int space2 = space1 >=0 ? line.indexOf(QLatin1Char(' '),space1+1) : -1; + QString mapversions = line.mid(space1+1,space2<0?line.length()-space1-2:space2-space1-1); + int dot = mapversions.indexOf(QLatin1Char('.')); + int dash = mapversions.indexOf(QLatin1Char('-')); + int mapvmaj = mapversions.left(dot).toInt(); + if (mapvmaj==vmaj) { + int mapvmin_from = (dash <= 0 ? mapversions.mid(dot+1) : mapversions.mid(dot+1,dash-dot-1)).toInt(); + int mapvmin_to = dash <= 0 ? mapvmin_from : mapversions.mid(dash+1).toInt(); + if (vmin >= mapvmin_from && vmin <= mapvmin_to) { + QStringRef mapfile = space2<0 ? QStringRef() : line.midRef(space2+1,line.length()-space2-2); + if (url_return) + *url_return = url.resolved(mapfile.toString()); + return true; + } } } - } - } while (!qmldir.atEnd()); + } while (!qmldir.atEnd()); + } + } else { + // XXX search non-files too! (eg. zip files, see QT-524) + QFileInfo f(url.toLocalFile()); + if (f.exists()) { + if (url_return) + *url_return = url; + return true; + } } - } else { - // XXX search non-files too! (eg. zip files, see QT-524) - QFileInfo f(url.toLocalFile()); - if (f.exists()) - return url; // (unversioned) local import } } - return QUrl(); - } - - QmlType *findBuiltin(const QByteArray& type, int *vmajor, int *vminor) const - { - for (int i=0; imajversions.prepend(vmaj); s->minversions.prepend(vmin); s->isLibrary.prepend(importType == QmlScriptParser::Import::Library); + s->isBuiltin.prepend(isbuiltin); return true; } - QUrl find(const QString& type) const - { - const QmlEnginePrivate::ImportedNamespace *s = 0; - int slash = type.indexOf(QLatin1Char('/')); - if (slash >= 0) { - while (!s) { - s = set.value(type.left(slash)); - int nslash = type.indexOf(QLatin1Char('/'),slash+1); - if (nslash > 0) - slash = nslash; - else - break; - } - } else { - s = &unqualifiedset; - } - QString unqualifiedtype = type.mid(slash+1); - if (s) - return s->find(unqualifiedtype); - else - return QUrl(); - } - - - QmlType *findBuiltin(const QByteArray& type, int *vmajor, int *vminor) + bool find(const QByteArray& type, int *vmajor, int *vminor, QmlType** type_return, QUrl* url_return) { QmlEnginePrivate::ImportedNamespace *s = 0; int slash = type.indexOf('/'); @@ -1298,10 +1273,16 @@ public: s = &unqualifiedset; } QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) - if (s) - return s->findBuiltin(unqualifiedtype,vmajor,vminor); - else - return 0; + if (s) { + if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return)) + return true; + } + if (url_return) { + *url_return = base.resolved(QUrl(QLatin1String(type + ".qml"))); + return true; + } else { + return false; + } } QmlEnginePrivate::ImportedNamespace *findNamespace(const QString& type) @@ -1413,40 +1394,27 @@ bool QmlEnginePrivate::addToImport(Imports* imports, const QString& uri, const Q */ bool QmlEnginePrivate::resolveType(const Imports& imports, const QByteArray& type, QmlType** type_return, QUrl* url_return, int *vmaj, int *vmin, ImportedNamespace** ns_return) const { - if (ns_return) { - *ns_return = imports.d->findNamespace(QLatin1String(type)); - if (*ns_return) - return true; - } - if (type_return) { - QmlType* t = imports.d->findBuiltin(type,vmaj,vmin); - if (!t) { - // XXX do we really still need this? - t = QmlMetaType::qmlType(type,0,0); // Try global namespace - if (vmin) *vmin = 0; - if (vmaj) *vmaj = 0; - } - if (t) { - if (type_return) *type_return = t; - if (qmlImportTrace()) - qDebug() << "QmlEngine::resolveType" << type << "= (builtin)"; - return true; - } + ImportedNamespace* ns = imports.d->findNamespace(QLatin1String(type)); + if (ns) { + if (qmlImportTrace()) + qDebug() << "QmlEngine::resolveType" << type << "is namespace for" << ns->urls; + if (ns_return) + *ns_return = ns; + return true; } - if (url_return) { - QUrl url = imports.d->find(QLatin1String(type)); - if (!url.isValid()) - url = imports.d->base.resolved(QUrl(QLatin1String(type + ".qml"))); - - if (url.isValid()) { - if (url_return) *url_return = url; - if (qmlImportTrace()) - qDebug() << "QmlEngine::resolveType" << type << "=" << url; + if (type_return || url_return) { + if (imports.d->find(type,vmaj,vmin,type_return,url_return)) { + if (qmlImportTrace()) { + if (type_return && *type_return) + qDebug() << "QmlEngine::resolveType" << type << "=" << (*type_return)->typeName(); + if (url_return) + qDebug() << "QmlEngine::resolveType" << type << "=" << *url_return; + } return true; } + if (qmlImportTrace()) + qDebug() << "QmlEngine::resolveType" << type << "not found"; } - if (qmlImportTrace()) - qDebug() << "QmlEngine::resolveType" << type << " not found"; return false; } @@ -1462,12 +1430,7 @@ bool QmlEnginePrivate::resolveType(const Imports& imports, const QByteArray& typ */ void QmlEnginePrivate::resolveTypeInNamespace(ImportedNamespace* ns, const QByteArray& type, QmlType** type_return, QUrl* url_return, int *vmaj, int *vmin ) const { - if (type_return) { - *type_return = ns->findBuiltin(type,vmaj,vmin); - } - if (url_return) { - *url_return = ns->find(QLatin1String(type)); - } + ns->find(type,vmaj,vmin,type_return,url_return); } QT_END_NAMESPACE diff --git a/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir b/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir index f22e179..ba0b42a 100644 --- a/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir +++ b/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir @@ -1,2 +1,3 @@ InstalledTest 1.0-3 InstalledTest.qml InstalledTest 1.4 InstalledTest2.qml +Rectangle 1.5 InstalledTest2.qml diff --git a/tests/auto/declarative/qmlparser/tst_qmlparser.cpp b/tests/auto/declarative/qmlparser/tst_qmlparser.cpp index c21d672..36471a4 100644 --- a/tests/auto/declarative/qmlparser/tst_qmlparser.cpp +++ b/tests/auto/declarative/qmlparser/tst_qmlparser.cpp @@ -54,6 +54,8 @@ private slots: void importsLocal(); void importsInstalled_data(); void importsInstalled(); + void importsOrder_data(); + void importsOrder(); // regression tests for crashes void crash1(); @@ -153,6 +155,8 @@ void tst_qmlparser::errors_data() QTest::newRow("failingComponent") << "failingComponentTest.qml" << "failingComponent.errors.txt" << false; QTest::newRow("missingSignal") << "missingSignal.qml" << "missingSignal.errors.txt" << false; QTest::newRow("finalOverride") << "finalOverride.qml" << "finalOverride.errors.txt" << false; + + QTest::newRow("importNamespaceConflict") << "importNamespaceConflict.qml" << "importNamespaceConflict.errors.txt" << false; } void tst_qmlparser::errors() @@ -694,6 +698,65 @@ void tst_qmlparser::importsInstalled() testType(qml,type); } + +void tst_qmlparser::importsOrder_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("type"); + + QTest::newRow("installed import overrides 1") << + "import com.nokia.installedtest 1.0\n" + "import com.nokia.installedtest 1.4\n" + "InstalledTest {}" + << "QFxText"; + QTest::newRow("installed import overrides 2") << + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "InstalledTest {}" + << "QFxRect"; + QTest::newRow("installed import re-overrides 1") << + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "import com.nokia.installedtest 1.4\n" + "InstalledTest {}" + << "QFxText"; + QTest::newRow("installed import re-overrides 2") << + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "InstalledTest {}" + << "QFxRect"; + + QTest::newRow("installed import versus builtin 1") << + "import com.nokia.installedtest 1.5\n" + "import Qt 4.6\n" + "Rectangle {}" + << "QFxRect"; + QTest::newRow("installed import versus builtin 2") << + "import Qt 4.6\n" + "import com.nokia.installedtest 1.5\n" + "Rectangle {}" + << "QFxText"; + QTest::newRow("namespaces cannot be overridden by types 1") << + "import Qt 4.6 as Rectangle\n" + "import com.nokia.installedtest 1.5\n" + "Rectangle {}" + << ""; + QTest::newRow("namespaces cannot be overridden by types 2") << + "import Qt 4.6 as Rectangle\n" + "import com.nokia.installedtest 1.5\n" + "Rectangle.Image {}" + << "QFxImage"; +} + +void tst_qmlparser::importsOrder() +{ + QFETCH(QString, qml); + QFETCH(QString, type); + testType(qml,type); +} + void tst_qmlparser::crash1() { QmlComponent component(&engine, "Component {}"); -- cgit v0.12