[SOLVED]Page wise scrolling in GridView
-
How can I make sure that scrolling snaps to certain page-borders when using a GridView?
Let's say I have a 4x3 grid of items in a grid view. When I now swipe to the left I want the view to snap to the next 4x3 grid and not just move one item to the left.
Currently I implemented this by nesting GridLayout items inside a ListView which has snap set to SnapOneItem. However this way it gets quite complicated to create a usable model in C++. I want the items inside this list to be taken from a contiguous list. But with the nesting I'd probably have to find a way of splitting the model into a model for each Grid inside the list. It sounds complex and I want to avoid that.A similar question was asked here for Symbian quite some time ago: http://qt-project.org/forums/viewthread/10743 Unfortunately no answer about the paging part yet.
-
With the help of a colleague I finally found a workable solution for this.
The basis is a QAbstractListModel that provides all the items that should be shown in the pages. I added three int Q_PROPERTYs called 'pages', 'columns' and 'rows' that are set by loading corresponding config files and by calculating the amount of 'pages' by dividing the total amount of items by the amount of items per page. This list is published towards QML using the setContextProperty() function.
@
//...
MySimpleList ui_mysimple_list;
engine.rootContext()->setContextProperty("ui_mysimple_list", &ui_mysimple_list);
//...
@To split this list into several pages I created a page model using an QAbstractProxyModel.
@
class MyPageModel : public QAbstractProxyModel
{
Q_OBJECTQ_PROPERTY(int pageNumber READ pageNumber WRITE setPageNumber NOTIFY pageNumberChanged) Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize NOTIFY pageSizeChanged) Q_PROPERTY(QObject* mainModel READ mainModel WRITE setMainModel)
signals:
void pageNumberChanged(int newPageNumber);
void pageSizeChanged(int newPageSize);public:
explicit MyPageModel(QObject *parent = 0);virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const { if (!sourceModel()) return QModelIndex(); int proxyRow = sourceIndex.row() % _pageSize; return index(proxyRow, sourceIndex.column(), QModelIndex()); } virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const { if (!sourceModel()) return QModelIndex(); int sourceRow = proxyIndex.row() + _pageNumber * _pageSize; return index(sourceRow, proxyIndex.column(), QModelIndex()); } virtual int columnCount(const QModelIndex &parent) const; virtual int rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); if (_pageNumber<0) return 0; if (!sourceModel()) return 0; int sourceCount = sourceModel()->rowCount(); int remainderSize = sourceCount - (_pageSize*_pageNumber); if (remainderSize<_pageSize) return remainderSize; return _pageSize; } virtual QModelIndex index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); return createIndex(row, column); } virtual QModelIndex parent(const QModelIndex &child) const; virtual QHash<int, QByteArray> roleNames() const { if (!sourceModel()) return QHash<int, QByteArray>(); return sourceModel()->roleNames(); } QObject *mainModel() const; int pageNumber() const; int pageSize() const;
public slots:
void setMainModel(QObject *mainModel);
void setPageNumber(int newPageNumber);
void setPageSize(int newPageSize);private:
int _pageNumber;
int _pageSize;
};
@This class is published towards QML as a constructable type:
@
qmlRegisterType<MyPageModel>("PackagePath", 1, 0, "MyPageModel");
@This combination can now easily be used in QML as follows:
@
Rectangle {
Item {
anchors.fill: parentListView { id: selection_list anchors.fill: parent interactive: ui_mysimple_list.pages > 1 // prevent scrolling when only one page is available orientation: Qt.Horizontal clip: true snapMode: ListView.SnapOneItem model: ui_mysimple_list.pages // we only need the amount of pages to generate enough list items at this level delegate: selection_list_page } } Component { id: selection_list_page GridView { interactive: false // no scrolling inside the GridView model: MyPageModel { mainModel: ui_mysimple_list pageNumber: index pageSize: ui_mysimple_list.columns * ui_mysimple_list.rows } delegate: grid_delegate } } Component { id: grid_delegate // here comes your code to display one single list item }
}
@ -
With the help of a colleague I finally found a workable solution for this.
The basis is a QAbstractListModel that provides all the items that should be shown in the pages. I added three int Q_PROPERTYs called 'pages', 'columns' and 'rows' that are set by loading corresponding config files and by calculating the amount of 'pages' by dividing the total amount of items by the amount of items per page. This list is published towards QML using the setContextProperty() function.
@
//...
MySimpleList ui_mysimple_list;
engine.rootContext()->setContextProperty("ui_mysimple_list", &ui_mysimple_list);
//...
@To split this list into several pages I created a page model using an QAbstractProxyModel.
@
class MyPageModel : public QAbstractProxyModel
{
Q_OBJECTQ_PROPERTY(int pageNumber READ pageNumber WRITE setPageNumber NOTIFY pageNumberChanged) Q_PROPERTY(int pageSize READ pageSize WRITE setPageSize NOTIFY pageSizeChanged) Q_PROPERTY(QObject* mainModel READ mainModel WRITE setMainModel)
signals:
void pageNumberChanged(int newPageNumber);
void pageSizeChanged(int newPageSize);public:
explicit MyPageModel(QObject *parent = 0);virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const { if (!sourceModel()) return QModelIndex(); int proxyRow = sourceIndex.row() % _pageSize; return index(proxyRow, sourceIndex.column(), QModelIndex()); } virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const { if (!sourceModel()) return QModelIndex(); int sourceRow = proxyIndex.row() + _pageNumber * _pageSize; return index(sourceRow, proxyIndex.column(), QModelIndex()); } virtual int columnCount(const QModelIndex &parent) const; virtual int rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); if (_pageNumber<0) return 0; if (!sourceModel()) return 0; int sourceCount = sourceModel()->rowCount(); int remainderSize = sourceCount - (_pageSize*_pageNumber); if (remainderSize<_pageSize) return remainderSize; return _pageSize; } virtual QModelIndex index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); return createIndex(row, column); } virtual QModelIndex parent(const QModelIndex &child) const; virtual QHash<int, QByteArray> roleNames() const { if (!sourceModel()) return QHash<int, QByteArray>(); return sourceModel()->roleNames(); } QObject *mainModel() const; int pageNumber() const; int pageSize() const;
public slots:
void setMainModel(QObject *mainModel);
void setPageNumber(int newPageNumber);
void setPageSize(int newPageSize);private:
int _pageNumber;
int _pageSize;
};
@This class is published towards QML as a constructable type:
@
qmlRegisterType<MyPageModel>("PackagePath", 1, 0, "MyPageModel");
@This combination can now easily be used in QML as follows:
@
Rectangle {
Item {
anchors.fill: parentListView { id: selection_list anchors.fill: parent interactive: ui_mysimple_list.pages > 1 // prevent scrolling when only one page is available orientation: Qt.Horizontal clip: true snapMode: ListView.SnapOneItem model: ui_mysimple_list.pages // we only need the amount of pages to generate enough list items at this level delegate: selection_list_page } } Component { id: selection_list_page GridView { interactive: false // no scrolling inside the GridView model: MyPageModel { mainModel: ui_mysimple_list pageNumber: index pageSize: ui_mysimple_list.columns * ui_mysimple_list.rows } delegate: grid_delegate } } Component { id: grid_delegate // here comes your code to display one single list item }
}
@