Pages
Developer Documentation
On this page

Laying out your content

Now that we understand pages, it is time to put things on them. This section goes through some important layout components and elements for designing our application.

By the end of it you'll have a neat-looking application.

ListViews

If you've used SonicDE's System Settings, you have come across a ListView. Put simply, it shows data in a list.

pageStack.initialPage: Kirigami.ScrollablePage {
    // ...
    Kirigami.CardsListView {
       id: cardsView
        model: kountdownModel
        delegate: kountdownDelegate
    }
}

That looks cryptic, but don't worry. Let's start from the beginning.

We add this component inside the Kirigami.ScrollablePage from the last step.

We use Kirigami.CardsListView, a ListView that makes it easy to show cards in a list. ListViews show data taken from a model: they fill themselves from the data we point them to. That is the model property, which here points to kountdownModel.

Model

Kirigami.ApplicationWindow {
    // ...
    ListModel {
        id: kountdownModel
        // Each ListElement is an element on the list, containing information
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }
    // ...
}

We add kountdownModel inside the Kirigami.ApplicationWindow from the last step.

A model defines how a data entry is structured. Our kountdownModel has a single element for now. Its ListElement shows the structure of the data: a name, a description and a date. That isn't fixed; your model can hold other kinds of data. The first two are strings, and the third is a number we use as a placeholder for now.

Note: QML builds on JavaScript, so many of the language's features are available in QML files. But QML variables must be declared with property, except inside JavaScript code blocks. See JavaScript in QML.

Models can also be changed with several methods. Some important ones:

Delegates

kountdownModel holds the data to show; kountdownDelegate decides how it is shown in the ListView. The Kirigami.CardsListView is made for card delegates, and our delegates are drawn as Kirigami.AbstractCards.

Delegates automatically get the properties of the model's ListElements, so we can refer to name, description and date like ordinary variables in the delegate.

Building the delegate card

The Component that is our delegate goes inside the Kirigami.ApplicationWindow. Then we look at what each part does.

Kirigami.ApplicationWindow {
    // ...
    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                // implicitWidth/Height define the natural width/height
                // of an item if no width or height is specified.
                // The setting below defines a component's preferred size based on its content
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        level: 1
                        text: date
                    }

                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        Controls.Label {
                            Layout.fillWidth: true
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        Layout.columnSpan: 2
                        text: i18nc("@action:button","Edit")
                        // onClicked: to be done... soon!
                    }
                }
            }
        }
    }
    // ...
}

implicitWidth and implicitHeight

First, how the component's width and height are managed:

Kirigami.AbstractCard {
    contentItem: Item {
        implicitWidth: delegateLayout.implicitWidth
        implicitHeight: delegateLayout.implicitHeight
        GridLayout {
            id: delegateLayout
            // ...
        }
    }
}

The first properties set in our Kirigami.AbstractCard's content are implicitWidth and implicitHeight, set to those of the GridLayout, delegateLayout.implicitWidth and delegateLayout.implicitHeight.

Every Item has an implicit width and height: hints used as the default, or fallback, when no explicit width or height is set. They default to 0×0, so it is very important to set them for plain Items like this one.

Setting them to the GridLayout's makes sure the content doesn't spill out of the card: the card takes as much space as its content needs.

Layouts

The GridLayout is inside the Item we gave as contentItem: the Item holding what the card shows.

We also need a layout, so the components don't pile up on top of each other. There are three main kinds:

  • ColumnLayout arranges components vertically, in one column;
  • RowLayout arranges them horizontally, in one row;
  • GridLayout arranges them in a grid of your choice.

With ColumnLayout and RowLayout, you just write the components inside the layout. We chose a GridLayout, which takes a bit more work:

GridLayout {
    id: delegateLayout
    anchors {
        left: parent.left
        top: parent.top
        right: parent.right
    }
    rowSpacing: Kirigami.Units.largeSpacing
    columnSpacing: Kirigami.Units.largeSpacing
    columns: root.wideScreen ? 4 : 2
    // ...
}

First the anchors. Qt Quick's anchoring positions components relative to parts of their parent. We anchor the GridLayout to the left, top and right of the card, so the content stretches across the whole card.

Then we set the spacing between the grid's rows and columns, so the components don't bunch up. Kirigami has predefined units for this. Their default values are:

Kirigami unit Pixels
smallSpacing 4
mediumSpacing 6
largeSpacing 8
gridUnit 18

Note: The design guidelines say more about the units and how to use them.

root is the id of our Kirigami.ApplicationWindow. Its wideScreen property tells whether the screen is wide (a computer monitor, or a phone in landscape). The conditional operator chooses the number of grid columns accordingly: 4 on a wide screen, 2 otherwise.

Inner components

We could just put three labels in the delegate, but that wouldn't look nice. We use a few more convenient components:

GridLayout {
    // ...
    Kirigami.Heading {
        level: 1
        text: date
    }

    ColumnLayout {
        Kirigami.Heading {
            Layout.fillWidth: true
            level: 2
            text: name
        }

        Kirigami.Separator {
            Layout.fillWidth: true
            visible: description.length > 0
        }

        Controls.Label {
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            text: description
            visible: description.length > 0
        }
    }

    Controls.Button {
        Layout.alignment: Qt.AlignRight
        Layout.columnSpan: 2
        text: i18nc("@action:button","Edit")
    }
}

Our application so far

Main.qml:

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    id: root

    width: 400
    height: 300

    title: i18nc("@title:window", "Day Kountdown")

    // ListModel needed for ListView, contains elements to be displayed
    ListModel {
        id: kountdownModel
        // Each ListElement is an element on the list, containing information
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }

    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        // Level determines the size of the heading
                        level: 1
                        text: date
                    }

                    // Layout for positioning elements vertically
                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        // Horizontal rule
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        // Labels contain text
                        Controls.Label {
                            Layout.fillWidth: true
                            // Word wrap makes text stay within box and shift with size
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        // Column spanning within grid layout (vertically in this case)
                        Layout.columnSpan: 2
                        text: i18nc("@action:button","Edit")
                        //onClicked: to be done...
                    }
                }
            }
        }
    }

    pageStack.initialPage: Kirigami.ScrollablePage {
        title: i18nc("@title", "Kountdown")

        // List view for card elements
        Kirigami.CardsListView {
            id: cardsView
            // Model contains info to be displayed
            model: kountdownModel
            // Delegate is how the information will be presented in the ListView
            delegate: kountdownDelegate
        }
    }
}

That's our basic card! With these steps we have laid the groundwork for adding all of the application's functionality.

Adapted for SonicDE from develop.kde.org/docs/getting-started/kirigami/introduction-listviews/ by Antti Savolainen, Carl Schwan, Claudio Cambra, Harshit Tomar, Michael Rostom, Mikhail Zolotukhin, Nicolas Fella, Swapnil Tripathi, Thiago Sueto, Yuri Chornoivan, licensed under CC BY-SA 4.0.

Edit this page on GitHub

We use cookies to personalize content and to analyze our traffic. Please decide if you are willing to accept cookies from our website.