Skip to content
nucularmoo edited this page May 28, 2018 · 23 revisions

Views (P0 | 1h)

Tino: New suggestion. Views

Tino: Let's have this before the delegate part (previous session) and let's introduce List, Grid, Path views. Let's very shortly introduce delegates, highlight, header, footer, keyboard handling.

Explanation of the contents of a topic page @ Topic reference page

Back to Week 3

Objective: Using QML views

Tino: Using QML views

Beginner

  • What is a view?

Intermediate

  • What is a list, grid, and path views?
  • How delegates are created?
  • What is a cache buffer?
  • How keyboard is used in delegates?
  • How many items is shown by the view?
  • What is a hater and footer?

Expert

Omitted


Course material content

For dynamic views Qt Quick provides two commonly used types, ListView and GridView. They both inherit from the Flickable type, which enables users to scroll around in a larger data set. The third view provided by Quick is PathView. It can be described as a more powerful and customizable view, but as a result it is also slightly more complex. In section 3.00 we quickly introduced the ListView type, and now we will have a more thorough look into the three different views. We'll start by having a more in-depth look into ListView.

ListView

ListView is a simple type, and in many ways similar in usage to the Repeater , which was covered in section 3.00. The data presented comes from a model, and the view instantiates a delegate which is used to present the data. The model can be an actual model type, such as ListModel, XmlListModel, or a custom model defined in C++, or it can be a simple integer, as in the following example.

J: This might be the simple example we give already in 3.00 if we want to have a quick intro to views there already, will see.

ListView {
    anchors.fill: parent
    anchors.margins: 10
    clip: true
    model: 50
    delegate: numberDelegate
    spacing: 5
}

Component {
    id: numberDelegate

    Rectangle {
        width: 35
        height: 35
        color: "lightGreen"
        border.color: "black"

        Text {
            text: index
            font.pointSize: 12
            anchors.centerIn: parent
        }
    }
}

Basic ListView

J: Constantly moving thing in the material is probably annoying, might replace with just a picture.

In the above example we used an explicitly defined delegate. A more thorough introduction to delegates will follow later in this part of the course, but let's cover some basics here. The delegate is the third part of Quick's model-view concept. A view will visualize each item list according to the template defined by the delegate. Each delegate get access to a number of attached properties, both from the model and the view. For example, the ListView to which the delegate is bound is accessible from the delegate through the ListView.view property.

The clip property will ensure that any list items outside of the view will not be visible. If set to false, items will 'flow over' the view. It should be noted that we should avoid using clip in the delegate. If clip is enabled inside a delegate, each delegate will be batched separately (i.e. there will be an OpenGL state change between each batch), which affects the rendering performance. By allowing the view (parent of the delegates) to do the clipping, as in the above example, there will be only one batch in the best case.

There are plenty of other behaviours we can change as well, such as orientation of the list (ListView.Vertical vs ListView.Horizontal). All of these behaviours can be found in the ListView documentation.

Headers and Footers

Views allow visual customization through decoration properties, such as the header and footer. By binding an object (usually another visual object) to these properties, the views are decoratable. As an example, a footer may include a Rectangle type showcasing borders, or a header that displays a logo on top of the list. It should be noted that headers and footers don't respect the spacing property in ListView, and thus any desired spacing needs to be a included in the header/footer itself.

Window {
    visible: true
    width: 200
    height: 480
    title: qsTr("Hello World")

    ListView {
        anchors.fill: parent
        anchors.margins: 20
        clip: true
        model: 4
        delegate: numberDelegate
        spacing: 2
        header: headerComponent
        footer: footerComponent
    }

    Component {
        id: headerComponent
        Rectangle {
            width: ListView.view.width
            height: 20
            color: "lightBlue"
            Text { text: 'Header'; anchors.centerIn: parent; }
        }
    }

    Component {
        id: footerComponent
        Rectangle {
            width: ListView.view.width
            height: 20
            color: "lightGreen"
            Text { text: 'Footer'; anchors.centerIn: parent; }
        }
    }

    Component {
        id: numberDelegate
        Rectangle {
            width: ListView.view.width
            height: 40
            border.color: "black"
            Text { text: 'Item ' + index; anchors.centerIn: parent; }
        }
    }
}

header-footer

Keyboard navigation and highlighting

When using a keyboard to navigate in the ListView, some form of highlighting is necessary to tell which item is currently selected. Two things are necessary to allow keyboard navigation in the ListView. First, the view needs to be given keyboard focus with the property focus: true. Second, a special highlight delegate needs to be defined. This is demonstrated in the following example:

ListView {
    id: view
    anchors.fill: parent
    anchors.margins: 20
    clip: true
    model: 20
    delegate: numberDelegate
    spacing: 5
    highlight: highlightComponent
    focus: true
}

Component {
    id: highlightComponent
    Rectangle {
        color: "lightblue"
        radius: 10
    }
}

Component {
    id: numberDelegate
    Item {
        width: ListView.view.width
        height: 40
        Text {
            anchors.centerIn: parent
            font.pixelSize: 10
            text: index
        }
    }
}

The highlight delegate is given the x, y and height of the current item. If the width is not specified, the width of the current item is used.

Sections

Sometimes we don't want to just list the data, but divide it up in categories. For example, albums by artists, or employees under their departments. With ListView we can divide the displayed data into categories, or sections.

When using sections, two properties need to be considered. In ListView the property section.property defines which property is used to divide the data into sections. It's important to note that the model needs to be sorted so that each property forming a section is continuous. If a property forming the section appears in multiple non-continuos places in the model, the same section might appeart multiple times in the view. The second property to be considered is section.criteria, which is set to ViewSection.FullString by default. This means that the whole property is used as the section. If set to ViewSection.FirstCharacter only the first character in the property is used for the section, for example when dividing up a list of names to sections based on the first letter of the last name.

After the sections have been defined, they can be accessed from the items using the attached properties ListView.section, ListView.previousSection, and ListView.nextSection.

We can also assign a special section delegate to create a header before each section, by assigning it to the property section.delegate in the ListView.

In the following example the data model has a list of artist and their albums, which are divided into sections by artist.

ListView {
    anchors.fill: parent
    anchors.margins: 20
    clip: true
    model: albumsAndArtists
    delegate: albumDelegate
    section.property: "artist"
    section.delegate: sectionDelegate
}

Component {
    id: albumDelegate
    Item {
        width: ListView.view.width
        height: 20
        Text {
            anchors.left: parent.left
            anchors.verticalCenter: parent.verticalCenter
            anchors.leftMargin: 10
            font.pixelSize: 12
            text: album
        }
    }
}

Component {
    id: sectionDelegate
    Rectangle {
        width: ListView.view.width
        height: 20
        color: "lightblue"
        Text {
            anchors.left: parent.left
            anchors.verticalCenter: parent.verticalCenter
            anchors.leftMargin: 6
            font.pixelSize: 14
            text: section
        }
    }
}

ListModel {
    id: albumsAndArtists
    ListElement { album: "Crazy World"; artist: "Scorpions"; }
    ListElement { album: "Love at First Sting"; artist: "Scorpions"; }
    ListElement { album: "Agents of Fortune"; artist: "Blue Öyster Cult"; }
    ListElement { album: "Spectres"; artist: "Blue Öyster Cult"; }
    ListElement { album: "The Vale of Shadows"; artist: "Red Raven Down"; }
    ListElement { album: "Definitely Maybe"; artist: "Oasis"; }
    ListElement { album: "(What's the Story) Morning Glory?"; artist: "Oasis"; }
    ListElement { album: "Moving Pictures"; artist: "Rush"; }
    ListElement { album: "Dookie"; artist: "Green Day"; }
    ListElement { album: "Eclipse"; artist: "Amorphis"; }
    ListElement { album: "High Voltage"; artist: "AC/DC"; }
    ListElement { album: "Highway to Hell"; artist: "AC/DC"; }
    ListElement { album: "1984"; artist: "Van Halen"; }
}

sections

GridView

GridView is largely similar to ListView, and it's used in almost the same way. The main difference is that it doen't rely on spacing and size of delegates, and instead cellWidth and cellHeight are defined in the view. The header, footers, keyboard navigation with highlighting are all available in GridView. Orientation is set in the flow property, options being GridView.LeftToRight (default, grid is filled from left to right, and rows added to the bottom) and GridView.TopToBottom (grid is filled from top to bottom, and columns are added to the right. Scrolling is horizontal). The following example showcases the usage of GridView.

GridView {
    id: grid
    anchors.fill: parent
    cellWidth: 80;
    cellHeight: 80
    model: 100
    delegate: numberDelegate
    highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
    focus: true
}

Component {
    id: numberDelegate
    Item {
        width: grid.cellWidth
        height: grid.cellHeight
        Text {
            text: index
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
        }
    }
}

gridview

PathView

PathView will display the model data on a Path. A Path type contains segments that are defined types such as PathQuad or PathCurve.

This example shows items on a straight path:

PathView {
    id: view
    model: 20

    path: Path {
        startX: 0
        startY: height

        PathCurve {
            x: view.width
            y: 0
        }
    }
    delegate: Text {
        text: "Index " + index
    }
}

Instructions and description for the exercise of the topic


Exhaustive reference material mentioned in this topic

https://qmlbook.github.io/en/ch06/index.html
https://doc.qt.io/qt-5/qml-qtquick-listview.html
https://doc.qt.io/qt-5/qml-qtquick-gridview.html
https://doc.qt.io/qt-5/qml-qtquick-pathview.html

Further reading topics/links:

Clone this wiki locally