Dispositions, vues en liste et cartes

Comprendre les différentes façons de placer les éléments sur une page

Mise en page de votre contenu

Maintenant que nous comprenons le fonctionnement des pages, il est temps d'ajouter des éléments aux nôtres. Nous allons passer en revue un certain nombre de composants et d'éléments de mise en page importants qui nous seront utiles lors de la conception de notre application.

Ne soyez pas effrayé par les gros morceaux de code ! Nous allons passer en revue tout ce que nous n'avons pas encore abordé et à la fin de cette section, vous aurez une application tout à fait soignée.

Vues en liste

If you've ever used Discover, NeoChat, or Plasma's System Settings, you will have come across a ListView. Quite simply, it lets you display data on a list.

Kirigami.CardsListView {
    id: cardsView
    model: kountdownModel
    delegate: kountdownDelegate
}

Cela semble mystérieux mais ne vous inquiétez pas. Commençons par le début.

We add this component inside our Kirigami.ScrollablePage from the last tutorial.

We're using Kirigami.CardsListView , which is a ListView that allows us to easily display cards in a list. However, ListViews are made to show data taken from a model - to automatically populate itself from a set of data that we point it to. That's where the model property comes in: in this example, it's pointing to kountdownModel.

Modèle

ListModel {
    id: kountdownModel
    // Chaque « ListElement » est un élément de la liste, contenant des informations.
    ListElement {
        name: "Dog birthday!!"
        description: "Big doggo birthday blowout."
        date: 100
    }
}

We add our kountdownModel inside our Kirigami.ApplicationWindow from the last tutorial.

A model defines the way that a data entry is structured. Our kountdownModel will consist of only one element for now. By looking at our ListElement above, we can see how the data of our kountdownModel are structured: it contains a name, a description, and a date. This isn't set in stone, and you may have different sorts of data in your model. The first two are just strings, and the third is a number we're using as a placeholder.

Les modèles sont également utiles dans la mesure où ils peuvent être modifiés grâce à l'utilisation de plusieurs méthodes. En voici les plus importants :

Déléguer

While our kountdownModel contains the data that will be displayed, our kountdownDelegate will handle how the data will be displayed in the ListView. Kirigami.CardsListView was designed to display card-type delegates, and we have indeed used a Kirigami.AbstractCard element as our delegate in the excerpt above.

Les délégués reçoivent automatiquement les propriétés des objets « ListElement » que nous avons spécifié dans notre modèle. Nous pouvons donc simplement se référer aux propriétés « name », « description » et « date » de nos objets « ListElement », comme si elles étaient une variable conventionnelle dans notre délégué.

Construction de notre carte déléguée

The Component that will represent our delegate can be added inside our Kirigami.ApplicationWindow . We will then proceed by checking what each part of our delegate component does.

Component {
    id: kountdownDelegate
    Kirigami.AbstractCard {
        contentItem: Item {
            // implicitWidth/Height définissent la largeur/hauteur naturelle d'un élément si aucune
            // largeur ou hauteur n'est spécifiée. Le paramètre ci-dessous définit la taille
            // préférée d'un composant préférée d'un composant en fonction de son contenu.
            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 {
                    Layout.fillHeight: true
                    level: 1
                    text: (date < 100000) ? date : i18n("%1 days", Math.round((date-Date.now())/86400000))
                }

                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: i18n("Edit")
                    // onClicked : à faire... bientôt !
                }
            }
        }
    }
}

implicitWidth et implicitHeight

The first part we will take a look at is how to manage the width and height of our component:

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

Looking at our Kirigami.AbstractCard , the first properties we set are implicitWidth and implicitHeight. We have set these to the delegateLayout.implicitWidth and delegateLayout.implicitHeight, i.e. the implicitWidth and implicitHeight of the GridLayout element. Implicit widths and heights are properties that are set as a default, i.e. if there is no explicit width or height set for these components. We have therefore set the implicitWidth and implicitHeight of our Kirigami.AbstractCard to that of the GridLayout below to ensure it does not spill out of the card.

Dispositions

The GridLayout is inside the Item component we have provided for the property contentItem. This is the Item that contains what will be displayed in your card.

We also need to choose a layout for our components so that they don't just pile on top of each other. There are three main types that we can choose from:

  • ColumnLayout lays out your components vertically, in a single column
  • RowLayout lays out your components horizontally, in a single row
  • GridLayout lays out your components in a grid with a composition of your choosing

With ColumnLayout and RowLayout, all we have to do is write our components inside the Layout component. As you can see, we went with a GridLayout, which entails a bit more handiwork.

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
    ...
}

The first thing you see is our anchors. QtQuick's anchoring system provides a useful way of making sure your components are positioned in certain parts of a parent component. We have anchored our GridLayout to the left, top, and right of the parent card, ensuring our content stretches across the whole card.

Next we specify the spacing between the rows and columns within our grid, so that our components don't bunch up. Kirigami provides a number of handy predefined units to use for this purpose:

Unité de KirigamiPixels
Petit espacement4 px
Espacement large8 px
Unité de grille18 px

As you might remember, root is the id of our Kirigami.ApplicationWindow . It provides the wideScreen property, used to determine whether the current device screen is a widescreen (i.e. a computer monitor or a phone in landscape). We use a ternary conditional here to vary the number of columns in our grid depending on the screen we are using: if it's a widescreen, the grid will have 4 columns, else it will have 2.

Composants internes

Nous pourrions simplement créer trois étiquettes au sein de notre composant délégué et l'appeler une journée. Mais cela n'aura pas un bel aspect.

GridLayout {
    ...
    Kirigami.Heading {
        Layout.fillHeight: true
        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: i18n("Edit")
    }
}

Copie d'écran affichant à quoi ressemble une carte

  • Left, Kirigami.Heading : uses the ListElement's date as a level 1 heading.
  • Middle, ColumnLayout: has a Kirigami.Heading that displays the task name; a Kirigami.Separator , which provides the horizontal line; and a Controls.Label , that displays a task's optional description. The latter two components have a visible property, which checks if the description is empty or not and displays the components depending on the result of description.length > 0.
  • A droite, Controls.Button  : un bouton qui fera quelque chose... bientôt !

Notre application jusqu'à présent

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami

Kirigami.ApplicationWindow {
    id: root

    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 {
                        // Heading will be as tall as possible while respecting constraints
                        Layout.fillHeight: true
                        // 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: i18n("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
        }
    }
}

Capture d'écran de l'apparence de l'application après avoir terminé cette leçon

Ainsi, ceci est notre carte basique !

Avec ces étapes, nous avons maintenant posé les bases pour ajouter toutes les fonctionnalités à notre application.