diff options
author | Michael Brasser <michael.brasser@nokia.com> | 2009-09-08 22:46:10 (GMT) |
---|---|---|
committer | Michael Brasser <michael.brasser@nokia.com> | 2009-09-08 22:46:10 (GMT) |
commit | d29c604b0365dfbc20f8aea8d2cb63f621995219 (patch) | |
tree | 511904026a961cadf5e7e13ff90b7e9baad1efbf | |
parent | cce03d9a3c95dd81c31003e0a96c5bb3dca6f0c0 (diff) | |
parent | 7731e5f7d33d3ec251299c7651e777d7e0054573 (diff) | |
download | Qt-d29c604b0365dfbc20f8aea8d2cb63f621995219.zip Qt-d29c604b0365dfbc20f8aea8d2cb63f621995219.tar.gz Qt-d29c604b0365dfbc20f8aea8d2cb63f621995219.tar.bz2 |
Merge branch 'kinetic-declarativeui' of git@scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
24 files changed, 608 insertions, 54 deletions
diff --git a/doc/src/declarative/pics/qml-context-object.png b/doc/src/declarative/pics/qml-context-object.png Binary files differnew file mode 100644 index 0000000..1b91aff --- /dev/null +++ b/doc/src/declarative/pics/qml-context-object.png diff --git a/doc/src/declarative/pics/qml-context-tree.png b/doc/src/declarative/pics/qml-context-tree.png Binary files differnew file mode 100644 index 0000000..6bba5f4 --- /dev/null +++ b/doc/src/declarative/pics/qml-context-tree.png diff --git a/doc/src/declarative/pics/qml-context.png b/doc/src/declarative/pics/qml-context.png Binary files differnew file mode 100644 index 0000000..bdf2ecd --- /dev/null +++ b/doc/src/declarative/pics/qml-context.png diff --git a/doc/src/declarative/qmlformat.qdoc b/doc/src/declarative/qmlformat.qdoc index 9cfebf0..5013ee7 100644 --- a/doc/src/declarative/qmlformat.qdoc +++ b/doc/src/declarative/qmlformat.qdoc @@ -16,21 +16,148 @@ Much of a QML file consists of valid ECMAScript \e {Statement}s. Except where c by ECMAScript, C++ or QObject prevented it, the syntactic extensions introduced by QML are designed to look similar and fit well with existing ECMAScript syntax and concepts. +\section1 QML engine + +The \l {QmlEngine}{QML engine} executes a \l {QmlComponent}{QML document} in a +\l {QmlContext}{QML context} to produce a \l {QObject}{QML object}. A single QML +document may be executed in one or many contexts to produce many QML objects. A single +QML document may be executed many times in the same context to produce many QML objects. + +The QML engine provides the environment in which QML documents, contexts and objects +exist. It must exist before any of these structures can be created. If the engine is removed, +existing documents, contexts and objects are invalidated, but not destroyed. An invalid + +\list +\i \e {QML document} can no longer be used to create QML objects. +\i \e {QML context} can no longer host QML objects, new context properties cannot be added +and existing context properties cannot be modified. +\i \e {QML object} will no longer evaluate bindings or scripts. +\endlist + +A QML document is a block of QML source code. QML documents generally correspond to files stored +on a disk or network resource, but can be constructed directly from text data. Syntactically a QML +document is self contained; QML does \bold {not} have a preprocessor that modifies the document +before presentation to the compiler. Type references within a QML document are resolved based +exclusively on the import statements present in the document. + +A simple QML document looks like this: + +\table +\row +\o +\code +import Qt 4.6 + +Rectangle { + id: MyRect + width: 100; height: 100 + color: background +} +\endcode +\endtable + +To instantiate a QML object, a QML document is executed in a QML context. QML contexts are used by +programmers to pass data to a QML document. QML documents may include property bindings or +ECMAScript blocks that can contain variable references that need to be resolved. Each property +binding and ECMAScript block has an associated QML context that is used to resolve these references +that is determined by the QML context in which the document is executed. The example document above +contains one variable reference, \c background. + +Each QML context defines a scope for variable resolution and each may define local, named context +properties. A QML context may also have a \l {QmlContext::addDefaultObject()}{default object}, +which is an object whose properties are searched \e after the context properties when resolving a +variable name. QML contexts form a tree, starting from a root context that is provided by the QML +engine. When resolving variable references, the QML contexts are searched starting from the +QML objects containing context upwards towards the root context. + +Consider the following QML context tree. If the example QML document is executed in \c Context1, +the \c background variable will resolve to \c Context1's context property. If the document is +executed in \c Context2, the \c background variable will resolve to the root context's context +property. + +\image qml-context-tree.png + +While QML contexts can be created explicitly by the programmer to pass data into QML objects, +the QML engine also creates a new implicit QML context for every object it instantiates. +Property bindings and ECMAScript blocks in the document are associated with this QML engine +created context. Object ids that are defined in the document are added as context properties, and +their value is set to reference the appropriate object, and the instantiated QML object is set as +the context's default object. The following diagram shows the result of executing a simple QML +document. + +\image qml-context-object.png + +The blue rectangle in the diagram represents a property binding. Associated with each property +binding is the QML context to which it belongs, the object property to which it is bound and a +\e {scope object}. The scope object is usually, but not always, the object to which the bound +property belongs. The context properties, context default objects and the scope object are all +involved when resolving a variable name in a binding. The following psuedo code describes the +alogithm used: + +\table +\row +\o +\code +if (scopeObject.hasProperty(name)) + return scopeObject.property(name) + +foreach (context in contextChain) { + if (context.hasContextProperty(name) + return context.contextProperty(name) + + if (context.defaultObject.hasProperty(name)) + return context.defaultObject.property(name) +} +\endcode +\endtable + +QML supports two categories of types: \e builtin types and \e composite types. Builtin types are +those written in C++ and registered with the QML engine. Builtin types form the most basic +building blocks of QML. Composite types are constructed by composing other builtin or composite +types, property bindings and ECMAScript blocks together into a brand new type using the QML +language. Using a composite type is identical to using a builtin type. + +For example, Qt 4.6 includes a builtin type called \c Image that shows a bitmap image. The +\c Image type has \c width and \c height properties that control the size of the displayed image. +A simple composite type, that will be called \c SquareImage can be built that adds a \c size +property that sets both the width and the height. + +\table +\row +\o +\code +import Qt 4.6 +Image { + property int size + width: size + height: size +} +\endcode +\endtable + +To the QML engine, a composite type is just another QML document. When a composite type is +used the engine instantiates it just as it would any other document - by creating a new implicit +QML context and the object tree described by the document. The diagram below shows the +\c SquareImage composite type used from within another QML document. When instantiated, the +\c SquareImage object is created in its own QML context. Any property bindings sepecified in the +\c SquareImage composite type document are associated with this context. Property bindings created +in the outer document, however, are associated with its context, even those that are applied to the +created \c SquareImage object. That is, the \c size, \c source, \c width and \c height property +bindings all share a common \e {scope object}, but are owned by two different QML contexts. The +difference in containing context results in the \c Root variable resolving differently in the +different property bindings. + +\image qml-context.png + +\section1 Syntax + \section2 Commenting The commenting rules in QML are the same as for ECMAScript. Both \e {MultiLineComment} blocks and \e {SingleLineComment}'s are supported. -\section1 Definitions - -\e {QML interpreter}: QmlEngine - -\e {QML execution context}: QmlContext +\section2 QML Document -\e {inherited characteristic} - -\section1 QML Document - -\section2 Syntax +\section3 Syntax \e {QMLDocument} \bold {:} @@ -69,22 +196,16 @@ The commenting rules in QML are the same as for ECMAScript. Both \e {MultiLineC \e {DecimalLiteral} \bold {but not} with \e {ExponentPart} \endquotation -\section2 Semantics - -A QML document is the unit in which QML code may be passed to the QML interpreter. A QML document -is syntactically self contained. QML documents are evaluated by the QML interpreter in a QML -execution context to produce a single instantiated object of the type specified by -\e {QMLObjectDefinition}. +\section3 Semantics The \e {QMLImportList} is used to statically resolve type references used within the enclosing -QML document. The import list is \bold {not} an \e {inherited characteristic}; its scope of -influence is limited to structures logically contained by the document. +QML document. An import statement is used to bring a set of types into scope for a QML document. -\section1 Object Definition +\section2 Object Definition -\section2 Syntax +\section3 Syntax \e {QMLObjectDefinition} \bold {:} \quotation @@ -125,11 +246,11 @@ An import statement is used to bring a set of types into scope for a QML documen \endquotation -\section2 Semantics +\section3 Semantics -\section1 Object Extension +\section2 Object Extension -\section2 Syntax +\section3 Syntax \e {QMLObjectExtensionDefinition} \bold {:} \quotation @@ -171,14 +292,14 @@ An import statement is used to bring a set of types into scope for a QML documen \quotation \e {FunctionDeclaration} \bold {but not} \e {Identifier} \sub {opt} \endquotation -\section2 Semantics +\section3 Semantics -\section1 Binding Expression +\section2 Binding Expression -\section2 Syntax +\section3 Syntax \e {QMLBindingExpression} \bold {:} -\section2 Semantics +\section3 Semantics */ diff --git a/doc/src/declarative/qtdeclarative.qdoc b/doc/src/declarative/qtdeclarative.qdoc index 1b7644c..460819a 100644 --- a/doc/src/declarative/qtdeclarative.qdoc +++ b/doc/src/declarative/qtdeclarative.qdoc @@ -80,7 +80,7 @@ QML Reference: \list - \o \l {QML Format} + \o \l {QML Format Reference} \o \l {elements}{QML Elements} \endlist diff --git a/examples/declarative/listview/content/pics/add.png b/examples/declarative/listview/content/pics/add.png Binary files differnew file mode 100644 index 0000000..f29d84b --- /dev/null +++ b/examples/declarative/listview/content/pics/add.png diff --git a/examples/declarative/listview/content/pics/del.png b/examples/declarative/listview/content/pics/del.png Binary files differnew file mode 100644 index 0000000..1d753a3 --- /dev/null +++ b/examples/declarative/listview/content/pics/del.png diff --git a/examples/declarative/listview/content/pics/trash.png b/examples/declarative/listview/content/pics/trash.png Binary files differnew file mode 100644 index 0000000..2042595 --- /dev/null +++ b/examples/declarative/listview/content/pics/trash.png diff --git a/examples/declarative/listview/dynamic.qml b/examples/declarative/listview/dynamic.qml new file mode 100644 index 0000000..58ce4b4 --- /dev/null +++ b/examples/declarative/listview/dynamic.qml @@ -0,0 +1,91 @@ +import Qt 4.6 + +Item { + width: 300 + height: 300 + + ListModel { + id: FruitModel + ListElement { + name: "Apple" + cost: 2.45 + } + ListElement { + name: "Banana" + cost: 1.95 + } + ListElement { + name: "Cumquat" + cost: 3.25 + } + ListElement { + name: "Durian" + cost: 9.95 + } + ListElement { + name: "Elderberry" + cost: 0.05 + } + ListElement { + name: "Fig" + cost: 0.25 + } + } + + Component { + id: FruitDelegate + Item { + width: parent.width; height: 35 + Text { font.pixelSize: 24; text: name } + Text { font.pixelSize: 24; text: '$'+Number(cost).toFixed(2); anchors.right: ItemButtons.left } + Row { + id: ItemButtons + anchors.right: parent.right + width: childrenRect.width + Image { source: "content/pics/add.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.set(index,"cost",Number(cost)+0.25) } + } + Image { source: "content/pics/del.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.set(index,"cost",Number(cost)-0.25) } + } + Image { source: "content/pics/trash.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.remove(index) } + } + Column { + width: childrenRect.width + Image { source: "content/pics/moreUp.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.move(index,index-1,1) } + } + Image { source: "content/pics/moreDown.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.move(index,index+1,1) } + } + } + } + } + } + + ListView { + model: FruitModel + delegate: FruitDelegate + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: Buttons.top + } + + Row { + width: childrenRect.width + height: childrenRect.height + anchors.bottom: parent.bottom + id: Buttons + Image { source: "content/pics/add.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.append({"name":"Pizza", "cost":5.95}) } + } + Image { source: "content/pics/add.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.insert(0,{"name":"Pizza", "cost":5.95}) } + } + Image { source: "content/pics/trash.png" + MouseRegion { anchors.fill: parent; onClicked: FruitModel.clear() } + } + } +} diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index e63eb8b..9bf4b10 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -31,6 +31,7 @@ ParentChangeAction -> ParentAction VisualModel -> VisualDataModel Renamed properties: +Item: contents -> childrenRect MouseRegion: xmin -> minimumX MouseRegion: xmax -> maximumX MouseRegion: ymin -> minimumY diff --git a/src/declarative/fx/qfxitem.cpp b/src/declarative/fx/qfxitem.cpp index 5363148..3429010 100644 --- a/src/declarative/fx/qfxitem.cpp +++ b/src/declarative/fx/qfxitem.cpp @@ -235,10 +235,10 @@ QFxContents::QFxContents() : m_x(0), m_y(0), m_width(0), m_height(0) } /*! - \qmlproperty qreal Item::childrenRect.x - \qmlproperty qreal Item::childrenRect.y - \qmlproperty qreal Item::childrenRect.width - \qmlproperty qreal Item::childrenRect.height + \qmlproperty real Item::childrenRect.x + \qmlproperty real Item::childrenRect.y + \qmlproperty real Item::childrenRect.width + \qmlproperty real Item::childrenRect.height The childrenRect properties allow an item access to the geometry of its children. This property is useful if you have an item that needs to be diff --git a/src/declarative/fx/qfxlistview.cpp b/src/declarative/fx/qfxlistview.cpp index c24610f..6c0a83e 100644 --- a/src/declarative/fx/qfxlistview.cpp +++ b/src/declarative/fx/qfxlistview.cpp @@ -1507,7 +1507,8 @@ void QFxListView::itemsInserted(int modelIndex, int count) // Update the indexes of the following visible items. for (; index < d->visibleItems.count(); ++index) { FxListItem *listItem = d->visibleItems.at(index); - listItem->setPosition(listItem->position() + (pos - initialPos)); + if (listItem->item != d->currentItem->item) + listItem->setPosition(listItem->position() + (pos - initialPos)); if (listItem->index != -1) listItem->index += count; } diff --git a/src/declarative/fx/qfxvisualitemmodel.cpp b/src/declarative/fx/qfxvisualitemmodel.cpp index 82bec09..cac8b8d 100644 --- a/src/declarative/fx/qfxvisualitemmodel.cpp +++ b/src/declarative/fx/qfxvisualitemmodel.cpp @@ -243,6 +243,16 @@ public: QmlContext *m_context; QList<int> m_roles; QHash<int,QString> m_roleNames; + void ensureRoles() { + if (m_roles.isEmpty()) { + if (m_listModelInterface) { + m_roles = m_listModelInterface->roles(); + for (int ii = 0; ii < m_roles.count(); ++ii) + m_roleNames.insert(m_roles.at(ii), + m_listModelInterface->toString(m_roles.at(ii))); + } + } + } struct ObjectRef { ObjectRef(QObject *object=0) : obj(object), ref(1) {} @@ -375,6 +385,7 @@ int QFxVisualDataModelDataMetaObject::createProperty(const char *name, const cha return QmlOpenMetaObject::createProperty(name, type); } else { const QLatin1String sname(name); + data->m_model->ensureRoles(); for (QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); iter != data->m_model->m_roleNames.end(); ++iter) { @@ -397,6 +408,7 @@ QFxVisualDataModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro && data->m_model->m_modelList) { return data->m_model->m_modelList->at(data->m_index); } else if (data->m_model->m_listModelInterface) { + data->m_model->ensureRoles(); for (QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); iter != data->m_model->m_roleNames.end(); ++iter) { @@ -410,6 +422,7 @@ QFxVisualDataModelDataMetaObject::propertyCreated(int, QMetaPropertyBuilder &pro } } } else if (data->m_model->m_abstractItemModel) { + data->m_model->ensureRoles(); for (QHash<int, QString>::ConstIterator iter = data->m_model->m_roleNames.begin(); iter != data->m_model->m_roleNames.end(); ++iter) { @@ -560,12 +573,6 @@ void QFxVisualDataModel::setModel(const QVariant &model) if (object && (d->m_listModelInterface = qobject_cast<QListModelInterface *>(object))) { d->m_roles.clear(); d->m_roleNames.clear(); - if (d->m_listModelInterface) { - d->m_roles = d->m_listModelInterface->roles(); - for (int ii = 0; ii < d->m_roles.count(); ++ii) - d->m_roleNames.insert(d->m_roles.at(ii), - d->m_listModelInterface->toString(d->m_roles.at(ii))); - } QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)), this, SLOT(_q_itemsChanged(int,int,QList<int>))); diff --git a/src/declarative/qml/parser/qmljs.g b/src/declarative/qml/parser/qmljs.g index b0ef866..4ed75e8 100644 --- a/src/declarative/qml/parser/qmljs.g +++ b/src/declarative/qml/parser/qmljs.g @@ -80,6 +80,7 @@ %token T_DEBUGGER "debugger" %token T_RESERVED_WORD "reserved word" %token T_MULTILINE_STRING_LITERAL "multiline string literal" +%token T_COMMENT "comment" --- context keywords. %token T_PUBLIC "public" diff --git a/src/declarative/qml/parser/qmljsastfwd_p.h b/src/declarative/qml/parser/qmljsastfwd_p.h index f79cfc2..a6fee1d 100644 --- a/src/declarative/qml/parser/qmljsastfwd_p.h +++ b/src/declarative/qml/parser/qmljsastfwd_p.h @@ -62,9 +62,9 @@ namespace QmlJS { namespace AST { class SourceLocation { public: - SourceLocation(quint32 offset = 0, quint32 length = 0) + SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) : offset(offset), length(length), - startLine(0), startColumn(0) + startLine(line), startColumn(column) { } bool isValid() const { return length != 0; } diff --git a/src/declarative/qml/parser/qmljsengine_p.cpp b/src/declarative/qml/parser/qmljsengine_p.cpp index 02d9b9c..eab8944 100644 --- a/src/declarative/qml/parser/qmljsengine_p.cpp +++ b/src/declarative/qml/parser/qmljsengine_p.cpp @@ -178,6 +178,12 @@ Engine::~Engine() QSet<NameId> Engine::literals() const { return _literals; } +void Engine::addComment(int pos, int len, int line, int col) +{ if (len > 0) _comments.append(QmlJS::AST::SourceLocation(pos, len, line, col)); } + +QList<QmlJS::AST::SourceLocation> Engine::comments() const +{ return _comments; } + NameId *Engine::intern(const QChar *u, int s) { return const_cast<NameId *>(&*_literals.insert(NameId(u, s))); } diff --git a/src/declarative/qml/parser/qmljsengine_p.h b/src/declarative/qml/parser/qmljsengine_p.h index 5aea983..877fff2 100644 --- a/src/declarative/qml/parser/qmljsengine_p.h +++ b/src/declarative/qml/parser/qmljsengine_p.h @@ -143,6 +143,7 @@ class Engine Lexer *_lexer; NodePool *_nodePool; QSet<NameId> _literals; + QList<QmlJS::AST::SourceLocation> _comments; public: Engine(); @@ -150,6 +151,9 @@ public: QSet<NameId> literals() const; + void addComment(int pos, int len, int line, int col); + QList<QmlJS::AST::SourceLocation> comments() const; + NameId *intern(const QChar *u, int s); static QString toString(NameId *id); diff --git a/src/declarative/qml/parser/qmljslexer.cpp b/src/declarative/qml/parser/qmljslexer.cpp index 9da6ec0..3be1710 100644 --- a/src/declarative/qml/parser/qmljslexer.cpp +++ b/src/declarative/qml/parser/qmljslexer.cpp @@ -43,6 +43,8 @@ #include "config.h" #endif +#include <QDebug> + #include "qmljsengine_p.h" #include "qmljslexer_p.h" #include "qmljsgrammar_p.h" @@ -71,7 +73,7 @@ extern double integerFromString(const char *buf, int size, int radix); using namespace QmlJS; -Lexer::Lexer(Engine *eng) +Lexer::Lexer(Engine *eng, bool tokenizeComments) : driver(eng), yylineno(0), done(false), @@ -94,8 +96,9 @@ Lexer::Lexer(Engine *eng) check_reserved(true), parenthesesState(IgnoreParentheses), parenthesesCount(0), - prohibitAutomaticSemicolon(false) -{ + prohibitAutomaticSemicolon(false), + tokenizeComments(tokenizeComments) +{qDebug()<<"--- new lexer"; driver->setLexer(this); // allocate space for read buffers buffer8 = new char[size8]; @@ -647,22 +650,29 @@ int Lexer::lex() setDone(Other); } else state = Start; + qDebug() << "--- state is InSingleLineComment @" << startlineno << ":"<<startcolumn; + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); } else if (current == 0) { + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); setDone(Eof); } + break; case InMultiLineComment: if (current == 0) { setDone(Bad); err = UnclosedComment; errmsg = QLatin1String("Unclosed comment at end of file"); + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); } else if (isLineTerminator()) { shiftWindowsLineBreak(); yylineno++; } else if (current == '*' && next1 == '/') { state = Start; shift(1); + driver->addComment(startpos, tokenLength(), startlineno, startcolumn); } + break; case InIdentifier: if (isIdentLetter(current) || isDecimalDigit(current)) { diff --git a/src/declarative/qml/parser/qmljslexer_p.h b/src/declarative/qml/parser/qmljslexer_p.h index 5817868..6cca45d 100644 --- a/src/declarative/qml/parser/qmljslexer_p.h +++ b/src/declarative/qml/parser/qmljslexer_p.h @@ -67,7 +67,7 @@ class NameId; class Lexer { public: - Lexer(Engine *eng); + Lexer(Engine *eng, bool tokenizeComments = false); ~Lexer(); void setCode(const QString &c, int lineno); @@ -239,6 +239,7 @@ private: ParenthesesState parenthesesState; int parenthesesCount; bool prohibitAutomaticSemicolon; + bool tokenizeComments; }; } // namespace QmlJS diff --git a/src/declarative/qml/qmetaobjectbuilder_p.h b/src/declarative/qml/qmetaobjectbuilder_p.h index d503163..c0b7426 100644 --- a/src/declarative/qml/qmetaobjectbuilder_p.h +++ b/src/declarative/qml/qmetaobjectbuilder_p.h @@ -68,7 +68,7 @@ class QMetaPropertyBuilderPrivate; class QMetaEnumBuilder; class QMetaEnumBuilderPrivate; -class Q_CORE_EXPORT QMetaObjectBuilder +class Q_DECLARATIVE_EXPORT QMetaObjectBuilder { public: enum AddMember @@ -189,7 +189,7 @@ private: friend class QMetaEnumBuilder; }; -class Q_CORE_EXPORT QMetaMethodBuilder +class Q_DECLARATIVE_EXPORT QMetaMethodBuilder { public: QMetaMethodBuilder() : _mobj(0), _index(0) {} @@ -227,7 +227,7 @@ private: QMetaMethodBuilderPrivate *d_func() const; }; -class Q_CORE_EXPORT QMetaPropertyBuilder +class Q_DECLARATIVE_EXPORT QMetaPropertyBuilder { public: QMetaPropertyBuilder() : _mobj(0), _index(0) {} @@ -278,7 +278,7 @@ private: QMetaPropertyBuilderPrivate *d_func() const; }; -class Q_CORE_EXPORT QMetaEnumBuilder +class Q_DECLARATIVE_EXPORT QMetaEnumBuilder { public: QMetaEnumBuilder() : _mobj(0), _index(0) {} diff --git a/src/declarative/util/qmllistmodel.cpp b/src/declarative/util/qmllistmodel.cpp index 0d9ea94..5b7d2bb 100644 --- a/src/declarative/util/qmllistmodel.cpp +++ b/src/declarative/util/qmllistmodel.cpp @@ -72,7 +72,9 @@ struct ListModelData \qmlclass ListModel \brief The ListModel element defines a free-form list data source. - The ListModel is a simple hierarchy of elements containing data roles. + The ListModel is a simple hierarchy of elements containing data roles. The contents can + be defined dynamically, or explicitly in QML: + For example: \code @@ -166,6 +168,29 @@ struct ListModelData } \endcode + The content of a ListModel may be created and modified using the clear(), + append(), and set() methods. For example: + + \code + Component { + id: FruitDelegate + Item { + width: 200; height: 50 + Text { text: name } + Text { text: '$'+cost; anchors.right: parent.right } + + // Double the price when clicked. + MouseRegion { + anchors.fill: parent + onClicked: FruitModel.set(index, "cost", cost*2) + } + } + } + \endcode + + When creating content dynamically, note that the set of available properties cannot be changed + except by first clearing the model - whatever properties are first added are then the + only permitted properties in the model. */ class ModelObject : public QObject @@ -189,7 +214,6 @@ struct ModelNode { ModelNode(); ~ModelNode(); - QString className; QList<QVariant> values; QHash<QString, ModelNode *> properties; @@ -214,6 +238,19 @@ struct ModelNode return objectCache; } + void setProperty(const QString& prop, const QVariant& val) { + QHash<QString, ModelNode *>::const_iterator it = properties.find(prop); + if (it != properties.end()) { + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode; + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toLatin1(), val); + } + QmlListModel *modelCache; ModelObject *objectCache; }; @@ -235,7 +272,7 @@ QmlListModel::~QmlListModel() void QmlListModel::checkRoles() const { - if (_rolesOk) + if (_rolesOk || !_root) return; for (int ii = 0; ii < _root->values.count(); ++ii) { @@ -341,6 +378,232 @@ int QmlListModel::count() const return _root->values.count(); } +/*! + \qmlmethod ListModel::clear() + + Deletes all content from the model. The properties are cleared such that + different properties may be set on subsequent additions. + + \sa append() remove() +*/ +void QmlListModel::clear() +{ + int cleared = count(); + _rolesOk = false; + delete _root; + _root = 0; + roleStrings.clear(); + emit itemsRemoved(0,cleared); +} + +/*! + \qmlmethod ListModel::remove(int index) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QmlListModel::remove(int index) +{ + if (_root) { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + _root->values.removeAt(index); + if (node) + delete node; + emit itemsRemoved(index,1); + } +} + +/*! + \qmlmethod ListModel::insert(index,dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + FruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is not in the list, sufficient empty items are + added to the list. + + \sa set() append() +*/ +void QmlListModel::insert(int index, const QVariantMap& valuemap) +{ + if (!_root) + _root = new ModelNode; + if (index >= _root->values.count()) { + set(index,valuemap); + return; + } + ModelNode *mn = new ModelNode; + for (QVariantMap::const_iterator it=valuemap.begin(); it!=valuemap.end(); ++it) { + addRole(it.key()); + ModelNode *value = new ModelNode; + value->values << it.value(); + mn->properties.insert(it.key(),value); + } + _root->values.insert(index,qVariantFromValue(mn)); + emit itemsInserted(index,1); +} + +/*! + \qmlmethod ListModel::move(from,to,n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + FruitModel.move(0,FruitModel.count-3,3) + \endcode + + \sa append() +*/ +void QmlListModel::move(int from, int to, int n) +{ + if (from+n > count() || to+n > count() || n==0 || from==to) + return; + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + if (n==1) { + _root->values.move(from,to); + } else { + QList<QVariant> replaced; + int i=0; + QVariantList::const_iterator it=_root->values.begin(); it += from+n; + for (; i<to-from; ++i,++it) + replaced.append(*it); + i=0; + it=_root->values.begin(); it += from; + for (; i<n; ++i,++it) + replaced.append(*it); + QVariantList::const_iterator f=replaced.begin(); + QVariantList::iterator t=_root->values.begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } + emit itemsMoved(from,to,n); +} + +/*! + \qmlmethod ListModel::append(dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + FruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set() remove() +*/ +void QmlListModel::append(const QVariantMap& valuemap) +{ + if (!_root) + _root = new ModelNode; + ModelNode *mn = new ModelNode; + for (QVariantMap::const_iterator it=valuemap.begin(); it!=valuemap.end(); ++it) { + addRole(it.key()); + ModelNode *value = new ModelNode; + value->values << it.value(); + mn->properties.insert(it.key(),value); + } + _root->values << qVariantFromValue(mn); + emit itemsInserted(count()-1,1); +} + +/*! + \qmlmethod ListModel::set(index,dict) + + Changes the item at \a index in the list model to the + values in \a dict. + + \code + FruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is not in the list, sufficient empty items are + added to the list. + + \sa append() +*/ +void QmlListModel::set(int index, const QVariantMap& valuemap) +{ + if (!_root) + _root = new ModelNode; + int initialcount = _root->values.count(); + while (index > _root->values.count()) + _root->values.append(qVariantFromValue(new ModelNode)); + if (index == _root->values.count()) + append(valuemap); + else { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + QList<int> roles; + for (QVariantMap::const_iterator it=valuemap.begin(); it!=valuemap.end(); ++it) { + node->setProperty(it.key(),it.value()); + int r = roleStrings.indexOf(it.key()); + if (r<0) { + r = roleStrings.count(); + roleStrings << it.key(); + } + roles.append(r); + } + if (initialcount < index) { + emit itemsInserted(initialcount,index-initialcount+1); + } else { + emit itemsChanged(index,1,roles); + } + } +} + +/*! + \qmlmethod ListModel::set(index,property,value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + FruitModel.set(3, "cost", 5.95) + \endcode + + If \a index is not in the list, sufficient empty items are + added to the list. + + \sa append() +*/ +void QmlListModel::set(int index, const QString& property, const QVariant& value) +{ + if (!_root) + _root = new ModelNode; + int initialcount = _root->values.count(); + while (index >= _root->values.count()) + _root->values.append(qVariantFromValue(new ModelNode)); + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + int r = roleStrings.indexOf(property); + if (r<0) { + r = roleStrings.count(); + roleStrings << property; + } + QList<int> roles; + roles.append(r); + + if (node) + node->setProperty(property,value); + if (initialcount < index) + emit itemsInserted(initialcount,index-initialcount+1); + else + emit itemsChanged(index,1,roles); +} + + class QmlListModelParser : public QmlCustomParser { public: @@ -518,7 +781,7 @@ static void dump(ModelNode *node, int ind) for (int ii = 0; ii < node->values.count(); ++ii) { ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii)); if (subNode) { - qWarning().nospace() << indent << "Sub-node " << ii << ": class " << subNode->className; + qWarning().nospace() << indent << "Sub-node " << ii; dump(subNode, ind + 1); } else { qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); diff --git a/src/declarative/util/qmllistmodel.h b/src/declarative/util/qmllistmodel.h index 39edbe4..8bef347 100644 --- a/src/declarative/util/qmllistmodel.h +++ b/src/declarative/util/qmllistmodel.h @@ -72,6 +72,14 @@ public: virtual int count() const; virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QVariantMap& valuemap); + Q_INVOKABLE void insert(int index, const QVariantMap& valuemap); + Q_INVOKABLE void set(int index, const QVariantMap& valuemap); + Q_INVOKABLE void set(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + private: QVariant valueForNode(ModelNode *) const; mutable QStringList roleStrings; diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 8bd3648..73ea75e 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -930,7 +930,7 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) } QGraphicsItem *lastSubFocusItem = subFocusItem; - if (subFocusItem && !inDestructor) { + if (subFocusItem) { // Update the child focus chain; when reparenting an item that has a // focus child, ensure that that focus child clears its focus child // chain from our parents before it's reparented. @@ -1204,6 +1204,8 @@ QGraphicsItem::~QGraphicsItem() Q_ASSERT(d_ptr->children.isEmpty()); } + d_ptr->subFocusItem = 0; + if (d_ptr->scene) { d_ptr->scene->d_func()->removeItemHelper(this); } else { diff --git a/tests/auto/declarative/qmlparser/listItemDeleteSelf.qml b/tests/auto/declarative/qmlparser/listItemDeleteSelf.qml new file mode 100644 index 0000000..fa2e831 --- /dev/null +++ b/tests/auto/declarative/qmlparser/listItemDeleteSelf.qml @@ -0,0 +1,38 @@ +import Qt 4.6 + +Item { + ListModel { + id: FruitModel + ListElement { + name: "Apple" + cost: 2.45 + } + ListElement { + name: "Orange" + cost: 3.25 + } + ListElement { + name: "Banana" + cost: 1.95 + } + } + + Component { + id: FruitDelegate + Item { + width: 200; height: 50 + Text { text: name } + Text { text: '$'+cost; anchors.right: parent.right } + MouseRegion { + anchors.fill: parent + onClicked: FruitModel.remove(index) + } + } + } + + ListView { + model: FruitModel + delegate: FruitDelegate + anchors.fill: parent + } +} |