src/declarative/util/qdeclarativelistmodel.cpp
changeset 33 3e2da88830cd
parent 30 5dc02b23752f
child 37 758a864f9613
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
    74     The ListModel is a simple hierarchy of elements containing data roles. The contents can
    74     The ListModel is a simple hierarchy of elements containing data roles. The contents can
    75     be defined dynamically, or explicitly in QML:
    75     be defined dynamically, or explicitly in QML:
    76 
    76 
    77     For example:
    77     For example:
    78 
    78 
    79     \code
    79     \snippet doc/src/snippets/declarative/listmodel.qml 0
    80     ListModel {
    80 
    81         id: fruitModel
    81     Roles (properties) must begin with a lower-case letter. The above example defines a
    82         ListElement {
       
    83             name: "Apple"
       
    84             cost: 2.45
       
    85         }
       
    86         ListElement {
       
    87             name: "Orange"
       
    88             cost: 3.25
       
    89         }
       
    90         ListElement {
       
    91             name: "Banana"
       
    92             cost: 1.95
       
    93         }
       
    94     }
       
    95     \endcode
       
    96 
       
    97     Roles (properties) must begin with a lower-case letter.The above example defines a
       
    98     ListModel containing three elements, with the roles "name" and "cost".
    82     ListModel containing three elements, with the roles "name" and "cost".
    99 
    83 
   100     Values must be simple constants - either strings (quoted), bools (true, false), numbers,
    84     Values must be simple constants - either strings (quoted and optionally within a call to QT_TR_NOOP),
   101     or enum values (like Text.AlignHCenter).
    85     bools (true, false), numbers, or enum values (like Text.AlignHCenter).
   102 
    86 
   103     The defined model can be used in views such as ListView:
    87     The defined model can be used in views such as ListView:
   104     \code
    88 
   105     Component {
    89     \snippet doc/src/snippets/declarative/listmodel-simple.qml 0
   106         id: fruitDelegate
    90     \dots 8
   107         Item {
    91     \snippet doc/src/snippets/declarative/listmodel-simple.qml 1
   108             width: 200; height: 50
    92     \image listmodel.png
   109             Text { text: name }
       
   110             Text { text: '$'+cost; anchors.right: parent.right }
       
   111         }
       
   112     }
       
   113 
       
   114     ListView {
       
   115         model: fruitModel
       
   116         delegate: fruitDelegate
       
   117         anchors.fill: parent
       
   118     }
       
   119     \endcode
       
   120 
    93 
   121     It is possible for roles to contain list data.  In the example below we create a list of fruit attributes:
    94     It is possible for roles to contain list data.  In the example below we create a list of fruit attributes:
       
    95 
       
    96     \snippet doc/src/snippets/declarative/listmodel-nested.qml model
       
    97 
       
    98     The delegate below displays all the fruit attributes:
       
    99 
       
   100     \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
       
   101     \image listmodel-nested.png
       
   102 
       
   103 
       
   104     \section2 Modifying list models
       
   105 
       
   106     The content of a ListModel may be created and modified using the clear(),
       
   107     append(), set() and setProperty() methods.  For example:
       
   108     
       
   109     \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate
       
   110 
       
   111     When creating content dynamically, note that the set of available properties cannot be changed
       
   112     except by first clearing the model. Whatever properties are first added to the model are then the
       
   113     only permitted properties in the model until it is cleared.
       
   114 
       
   115 
       
   116     \section2 Using threaded list models with WorkerScript
       
   117 
       
   118     ListModel can be used together with WorkerScript access a list model
       
   119     from multiple threads. This is useful if list modifications are
       
   120     synchronous and take some time: the list operations can be moved to a
       
   121     different thread to avoid blocking of the main GUI thread.
       
   122 
       
   123     Here is an example that uses WorkerScript to periodically append the
       
   124     current time to a list model:
       
   125 
       
   126     \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0
       
   127 
       
   128     The included file, \tt dataloader.js, looks like this:
       
   129 
       
   130     \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
       
   131 
       
   132     The application's \tt Timer object periodically sends a message to the
       
   133     worker script by calling \l WorkerScript::sendMessage(). When this message
       
   134     is received, \l {WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in
       
   135     \tt dataloader.js, which appends the current time to the list model.
       
   136 
       
   137     Note the call to sync() from the \l {WorkerScript::onMessage}{WorkerScript.onMessage()} handler.
       
   138     You must call sync() or else the changes made to the list from the external
       
   139     thread will not be reflected in the list model in the main thread.
       
   140 
       
   141     \section3 Limitations
       
   142 
       
   143     If a list model is to be accessed from a WorkerScript, it \bold cannot
       
   144     contain list data. So, the following model cannot be used from a WorkerScript
       
   145     because of the list contained in the "attributes" property:
   122 
   146 
   123     \code
   147     \code
   124     ListModel {
   148     ListModel {
   125         id: fruitModel
   149         id: fruitModel
   126         ListElement {
   150         ListElement {
   129             attributes: [
   153             attributes: [
   130                 ListElement { description: "Core" },
   154                 ListElement { description: "Core" },
   131                 ListElement { description: "Deciduous" }
   155                 ListElement { description: "Deciduous" }
   132             ]
   156             ]
   133         }
   157         }
   134         ListElement {
       
   135             name: "Orange"
       
   136             cost: 3.25
       
   137             attributes: [
       
   138                 ListElement { description: "Citrus" }
       
   139             ]
       
   140         }
       
   141         ListElement {
       
   142             name: "Banana"
       
   143             cost: 1.95
       
   144             attributes: [
       
   145                 ListElement { description: "Tropical" },
       
   146                 ListElement { description: "Seedless" }
       
   147             ]
       
   148         }
       
   149     }
   158     }
   150     \endcode
   159     \endcode
   151 
   160 
   152     The delegate below will list all the fruit attributes:
       
   153     \code
       
   154     Component {
       
   155         id: fruitDelegate
       
   156         Item {
       
   157             width: 200; height: 50
       
   158             Text { id: name; text: name }
       
   159             Text { text: '$'+cost; anchors.right: parent.right }
       
   160             Row {
       
   161                 anchors.top: name.bottom
       
   162                 spacing: 5
       
   163                 Text { text: "Attributes:" }
       
   164                 Repeater {
       
   165                     model: attributes
       
   166                     Component { Text { text: description } }
       
   167                 }
       
   168             }
       
   169         }
       
   170     }
       
   171     \endcode
       
   172 
       
   173     \section2 Modifying list models
       
   174 
       
   175     The content of a ListModel may be created and modified using the clear(),
       
   176     append(), and set() methods.  For example:
       
   177 
       
   178     \code
       
   179     Component {
       
   180         id: fruitDelegate
       
   181         Item {
       
   182             width: 200; height: 50
       
   183             Text { text: name }
       
   184             Text { text: '$'+cost; anchors.right: parent.right }
       
   185 
       
   186             // Double the price when clicked.
       
   187             MouseArea {
       
   188                 anchors.fill: parent
       
   189                 onClicked: fruitModel.set(index, "cost", cost*2)
       
   190             }
       
   191         }
       
   192     }
       
   193     \endcode
       
   194 
       
   195     When creating content dynamically, note that the set of available properties cannot be changed
       
   196     except by first clearing the model - whatever properties are first added are then the
       
   197     only permitted properties in the model.
       
   198 
       
   199 
       
   200     \section2 Using threaded list models with WorkerScript
       
   201 
       
   202     ListModel can be used together with WorkerScript access a list model
       
   203     from multiple threads. This is useful if list modifications are
       
   204     synchronous and take some time: the list operations can be moved to a
       
   205     different thread to avoid blocking of the main GUI thread.
       
   206 
       
   207     Here is an example that uses WorkerScript to periodically append the
       
   208     current time to a list model:
       
   209 
       
   210     \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0
       
   211 
       
   212     The included file, \tt dataloader.js, looks like this:
       
   213 
       
   214     \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
       
   215 
       
   216     The application's \tt Timer object periodically sends a message to the
       
   217     worker script by calling \tt WorkerScript::sendMessage(). When this message
       
   218     is received, \tt WorkerScript.onMessage() is invoked in
       
   219     \tt dataloader.js, which appends the current time to the list model.
       
   220 
       
   221     Note the call to sync() from the \c WorkerScript.onMessage() handler.
       
   222     You must call sync() or else the changes made to the list from the external
       
   223     thread will not be reflected in the list model in the main thread.
       
   224 
       
   225     \section3 Limitations
       
   226 
       
   227     If a list model is to be accessed from a WorkerScript, it \bold cannot
       
   228     contain list data. So, the following model cannot be used from a WorkerScript
       
   229     because of the list contained in the "attributes" property:
       
   230 
       
   231     \code
       
   232     ListModel {
       
   233         id: fruitModel
       
   234         ListElement {
       
   235             name: "Apple"
       
   236             cost: 2.45
       
   237             attributes: [
       
   238                 ListElement { description: "Core" },
       
   239                 ListElement { description: "Deciduous" }
       
   240             ]
       
   241         }
       
   242     }
       
   243     \endcode
       
   244 
       
   245     In addition, the WorkerScript cannot add any list data to the model.
   161     In addition, the WorkerScript cannot add any list data to the model.
   246 
   162 
   247     \sa {qmlmodels}{Data Models}, WorkerScript, QtDeclarative
   163     \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
   248 */
   164 */
   249 
   165 
   250 
   166 
   251 /*
   167 /*
   252     A ListModel internally uses either a NestedListModel or FlatListModel.
   168     A ListModel internally uses either a NestedListModel or FlatListModel.
   452 
   368 
   453     The from and to ranges must exist; for example, to move the first 3 items
   369     The from and to ranges must exist; for example, to move the first 3 items
   454     to the end of the list:
   370     to the end of the list:
   455 
   371 
   456     \code
   372     \code
   457         fruitModel.move(0,fruitModel.count-3,3)
   373         fruitModel.move(0, fruitModel.count - 3, 3)
   458     \endcode
   374     \endcode
   459 
   375 
   460     \sa append()
   376     \sa append()
   461 */
   377 */
   462 void QDeclarativeListModel::move(int from, int to, int n)
   378 void QDeclarativeListModel::move(int from, int to, int n)
   633 
   549 
   634         if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
   550         if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
   635             QDeclarativeCustomParserNode node =
   551             QDeclarativeCustomParserNode node =
   636                 qvariant_cast<QDeclarativeCustomParserNode>(value);
   552                 qvariant_cast<QDeclarativeCustomParserNode>(value);
   637 
   553 
   638             if (node.name() != "ListElement") {
   554             if (node.name() != listElementTypeName) {
   639                 error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
   555                 const QMetaObject *mo = resolveType(node.name());
   640                 return false;
   556                 if (mo != &QDeclarativeListElement::staticMetaObject) {
       
   557                     error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
       
   558                     return false;
       
   559                 }
       
   560                 listElementTypeName = node.name(); // cache right name for next time
   641             }
   561             }
   642 
   562 
   643             {
   563             {
   644             ListInstruction li;
   564             ListInstruction li;
   645             li.type = ListInstruction::Push;
   565             li.type = ListInstruction::Push;
   702                     d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list
   622                     d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list
   703                 } else {
   623                 } else {
   704                     QByteArray script = variant.asScript().toUtf8();
   624                     QByteArray script = variant.asScript().toUtf8();
   705                     int v = evaluateEnum(script);
   625                     int v = evaluateEnum(script);
   706                     if (v<0) {
   626                     if (v<0) {
   707                         error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value"));
   627                         if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) {
   708                         return false;
   628                             d[0] = char(QDeclarativeParser::Variant::String);
       
   629                             d += script.mid(12,script.length()-14);
       
   630                         } else {
       
   631                             error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value"));
       
   632                             return false;
       
   633                         }
   709                     } else {
   634                     } else {
   710                         d[0] = char(QDeclarativeParser::Variant::Number);
   635                         d[0] = char(QDeclarativeParser::Variant::Number);
   711                         d += QByteArray::number(v);
   636                         d += QByteArray::number(v);
   712                     }
   637                     }
   713                 }
   638                 }
   727 
   652 
   728 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps)
   653 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps)
   729 {
   654 {
   730     QList<ListInstruction> instr;
   655     QList<ListInstruction> instr;
   731     QByteArray data;
   656     QByteArray data;
       
   657     listElementTypeName = QByteArray(); // unknown
   732 
   658 
   733     for(int ii = 0; ii < customProps.count(); ++ii) {
   659     for(int ii = 0; ii < customProps.count(); ++ii) {
   734         const QDeclarativeCustomParserProperty &prop = customProps.at(ii);
   660         const QDeclarativeCustomParserProperty &prop = customProps.at(ii);
   735         if(prop.name() != "") { // isn't default property
   661         if(prop.name() != "") { // isn't default property
   736             error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name())));
   662             error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name())));
  1094 QVariant NestedListModel::data(int index, int role) const
  1020 QVariant NestedListModel::data(int index, int role) const
  1095 {
  1021 {
  1096     Q_ASSERT(_root && index >= 0 && index < _root->values.count());
  1022     Q_ASSERT(_root && index >= 0 && index < _root->values.count());
  1097     checkRoles();
  1023     checkRoles();
  1098     QVariant rv;
  1024     QVariant rv;
       
  1025     if (roleStrings.count() < role)
       
  1026         return rv;
  1099 
  1027 
  1100     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
  1028     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
  1101     if (!node)
  1029     if (!node)
  1102         return rv;
  1030         return rv;
  1103 
  1031