Since SelectionDialog seems to follow the QML Data model pattern, and it has its own delegate defined that handles the data for the platform, I thought it would be as easy as this:
PageStackWindow { id: appWindow initialPage: mainPage MainPage{id: mainPage} ToolBarLayout { id: commonTools visible: true ToolIcon { platformIconId: "toolbar-view-menu"; anchors.right: parent===undefined ? undefined : parent.right onClicked: (myMenu.status == DialogStatus.Closed) ? myMenu.open() : myMenu.close() } } Menu { id: myMenu visualParent: pageStack MenuLayout { MenuItem { text: "Sample menu item" } } } }
MainPage.qml:
Page { id: mainPage tools: commonTools SelectionDialog { id: selectionDialog titleText: "Select item" model: selectionModel } Button { text: "Select" onClicked: selectionDialog.open() } }
main.cpp:
#include <QtGui/QApplication> #include <QtDeclarative> int main(int argc, char *argv[]) { QApplication app(argc, argv); QStringList dataList; dataList.append("Item 1"); dataList.append("Item 2"); dataList.append("Item 3"); dataList.append("Item 4"); dataList.append("Item 5"); QStringListModel dataListModel(dataList); QDeclarativeView view; QDeclarativeContext *ctxt = view.rootContext(); ctxt->setContextProperty("selectionModel", &dataListModel); view.setSource(QUrl("qrc:/qml/main.qml")); view.showFullScreen(); return app.exec(); }
In these code samples, only MainPage.qml and main.cpp are interesting. I create a list of items I want to show in C++, assign them to a model so that it can be shown in QML, register the model as id so that it's available in QML and then load and show the QML. When pressing the "Select" button, the items should be shown.
But they don't show, instead I got an empty list. And there is at least two separate reasons why the items are not shown:
- Qt's models have "roles" which are used to access data in the model. SelectionDialog tries to display data from roles that are not set by QStringListModel. SelectionDialog uses data from role "name", but QStringListModel gives access to the data in the role "display".
- SelectionDialog's model property does not accept just any model, it expects QML ListModel type. QStringListModel can not be assigned to the QML property, trying to assign it will give an error.
If you want more information and discussion on those issues, see a posting on MeeGo Forum.
These issues, especially the second one, may be temporary problems that will be fixed in future. But for now, I'll show the cleanest way that I could figure out to handle this.
For mixed C++/QML programs I think the best practice is to have the C++ part of the code to be common as much as possible across platforms, and the QML part will be what is customized for each platform as necessary. So the C++ code I represent here hopefully works in MeeGo/Harmattan as well as in Symbian. For the QML part I will only represent the Harmattan part, as that's what I'm interested in and that's what I can test.
In a nut shell, I create in QML an empty ListModel, and signal from C++ to QML side to add elements to the ListModel using JavaScript. In the C++ code I create a wrapper for QStringListModel that sets the displayed role to "name" so that Meego Harmattan and Symbian Qt Component delegates can use it. In Symbian platforms, there should be no need for the "Connections" block in the QML, as you should be able to just assign the "selectionModel" to SelectionDialog's model directly.
There is one more caveat on Meego 1.2 Harmattan API. You need to set SelectionDialog's selectedIndex property to some value. If you do not set the selected index, initially the dialog does not show the first item. So for now displaying a dialog with this code is not possible without having a default selection.
I'll represent the code here, included with comments on the parts that need explaining. If you've figured a cleaner way to do this please post in the comments.
main.cpp:
#include <QtGui/QApplication> #include <QtDeclarative> #include "qstringlistmodelforqtcomponent.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QStringList dataList; QStringListModelForQtComponent dataListModel; dataList.append("Item 1"); dataList.append("Item 2"); dataList.append("Item 3"); dataList.append("Item 4"); dataList.append("Item 5"); QDeclarativeView view; QDeclarativeContext *ctxt = view.rootContext(); ctxt->setContextProperty("selectionModel", &dataListModel); view.setSource(QUrl("qrc:/qml/main.qml")); // The displayed elements must be set after the QML has been // loaded, as on MeeGo/Harmattan adding the elements to // lists is done by emitting signals. The signal handlers // are not instantiated before the QML has been loaded. // Doing it this way should also work on Symbian // Qt Components and the stock ListView because QML models // are updated on the fly if they are changed. dataListModel.setStringList(dataList); view.showFullScreen(); dataListModel.setStringList(dataList); return app.exec(); }qstringlistmodelforqtcomponent.h:
#ifndef Q_STRING_LIST_MODEL_FOR_QT_COMPONENT_H #define Q_STRING_LIST_MODEL_FOR_QT_COMPONENT_H #include <QObject> #include <QStringListModel> class QStringListModelForQtComponent : public QStringListModel { Q_OBJECT public: // Contrary to normal QStringListModel, this class does not // allow giving the strings in the model in constructor. // The reason is that for this model to work also on // MeeGo/Harmattan, the strings must be set after the // QML template has been loaded. Otherwise the signal // handlers in QML have not been registered. The default // role name used to display the roles is "name", which is // used both by Symbian and Harmattan Qt Compontents. QStringListModelForQtComponent(const QByteArray &displayRoleName = "name", QObject *parent = 0); // Overrides QStringListModel's function. Setting this will // emit stringAdded(QString) for each string in the list. If // there was non empty string list set to this model before, // stringsReset() signal is emitted before // stringAdded(QString) signals. void setStringList(const QStringList &strings); signals: void stringAdded(const QString &newString); // Emitted when setStringList is called if already contained strings void stringsReset(); }; #endif // Q_STRING_LIST_MODEL_FOR_QT_COMPONENT_Hqstringlistmodelforqtcomponent.cpp:
#include "qstringlistmodelforqtcomponent.h" QStringListModelForQtComponent::QStringListModelForQtComponent(const QByteArray &displayRoleName, QObject *parent) : QStringListModel(parent) { QHash<int, QByteArray> roleNames; roleNames.insert(Qt::DisplayRole, displayRoleName); setRoleNames(roleNames); } void QStringListModelForQtComponent::setStringList(const QStringList &strings) { if (!stringList().isEmpty()) { emit stringsReset(); } QStringListModel::setStringList(strings); QStringList::const_iterator i = strings.constBegin(); for (; i != strings.constEnd(); ++i) { QString s = *i; emit stringAdded(s); } }MainPage.qml:
import QtQuick 1.1 import com.meego 1.0 Page { id: mainPage tools: commonTools SelectionDialog { id: selectionDialog titleText: "Select item" selectedIndex: 0 model: ListModel { // Populated from C++ backend } } Connections { target: selectionModel // Only onStringAdded signal is needed, since the C++ backend // needs only to initialize the strings once. onStringAdded: { selectionDialog.model.append({name: newString}) } } }