[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_OBJECT

    Q_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: parent

        ListView {
            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_OBJECT

    Q_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: parent

        ListView {
            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
    
    }
    

    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.