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

Si vous avez déjà utilisé Discover, NeoChat ou les configuration du système Plasma, vous avez forcément rencontré un affichage en liste. Ces derniers vous permettent tout simplement d'afficher des données dans une liste.

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

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

La première chose que vous remarquerez est que nous utilisons « Kirigami.CardsListView ». Il s'agit d'un objet « ListView » nous permettant d'afficher facilement des cartes dans une liste. Cependant, ces objets « ListViews » sont conçus pour afficher des données provenant d'un modèle - pour se remplir automatiquement à partir d'un ensemble de données que nous lui indiquons. C'est là que la propriété « model » intervient : dans cet exemple, elle pointe vers « 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 }
}

Un modèle définit la manière dont une entrée de données est structurée. En regardant notre objet « ListElement » ci-dessus, nous pouvons voir comment les éléments de l'objet « kountdownModel » sont structurés : ils contiennent un nom, une description et une date. Les deux premiers sont simplement des chaînes de caractères et le troisième est un nombre que nous utilisons comme caractère de remplacement.

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 :

  • La méthode « ListModelName.append(jsobject yourobject) » ajoute un objet JavaScript que vous fournissez à l'objet « ListModel » et le place après le dernier élément du modèle. Pour que cela se réalise correctement, vous devez fournir un objet JavaScript avec les propriétés correctes et les types de données correspondants.
  • ListModelName.get(int index) renvoie l'objet « JSObject » à l'emplacement de l'index que vous avez fourni.
  • La fonction « ListModelName.remove(int index, int count) » supprime l'objet « JSObject » à l'emplacement de l'index fourni, et autant après cet emplacement d'index que vous le souhaitez (1 adresse seulement l'objet « JSObject » à l'indice fourni).
  • La fonction « ListModelName.set(int index, jsobject yourobject) » modifie l'élément à l'emplacement de l'index fourni avec les valeurs fournies dans « yourobject ». Les mêmes règles s'appliquent avec « .append ».

Déléguer

Le délégué gère la façon dont les données de votre objet « ListModel » seront affichées dans l'objet « ListView ». Les éléments « CardsListView » sont conçus avec des délégués de type carte en tête et nous avons effectivement utilisé un élément « Kirigami.AbstractCard » comme notre délégué comme indiqué dans l'extrait ci-dessus.

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

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

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

En regardant notre « Kirigami.AbstractCard », les premières propriétés que nous définissons sont « implicitWidth » et « implicitHeight ». Nous leur avons attribué les valeurs « delegateLayout.implicitWidth » et « delegateLayout.implicitHeight », c'est-à-dire les valeurs « implicitWidth » et « implicitHeight » de l'élément « GridLayout ». Les largeurs et hauteurs implicites sont des propriétés définies par défaut, c'est-à-dire si aucune largeur ou hauteur explicite ne sont définies pour ces composants. Nous avons donc défini les propriétés « implicitWidth » et « implicitHeight » de notre « Kirigami.AbstractCard » selon celles de l'élément « GridLayout » ci-dessous pour nous assurer que ce dernier ne déborde pas de la carte.

Dispositions

Le « GridLayout » se trouve à l'intérieur du composant « Item » que nous avons fourni pour la propriété « contentItem ». Il s'agit de l'élément contenant ce qui sera affiché dans votre carte.

Nous devons également choisir une disposition pour nos composants afin qu'ils ne s'empilent pas les uns sur les autres. Il existe trois types principaux parmi lesquels nous pouvons choisir :

  • « ColumnLayout » présente vos composants verticalement, dans une seule colonne.
  • « RowLayout » dispose vos composants horizontalement, sur une seule ligne.
  • « GridLayout » dispose vos composants dans une grille avec une disposition de votre choix.

Avec les paramètres « ColumnLayout » et « RowLayout », tout ce que nous avons à faire est d'écrire nos composants à l'intérieur du composant « Layout ». Comme vous pouvez le constater, nous avons opté pour une disposition en grille, ce qui implique un peu plus de travail manuel.

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

La première chose que vous voyez est notre « ancre ». Le système d'ancrage de QtQuick fournit un moyen utile de s'assurer que vos composants sont positionnés sur certaines parties d'un composant parent. Nous avons ancré notre objet « GridLayout » à gauche, en haut et à droite de la carte parente, en nous assurant que notre contenu s'étende sur la totalité de la carte.

Ensuite, nous spécifions l'espacement entre les lignes et les colonnes de notre grille, pour que nos composants ne s'entassent pas. Kirigami fournit un certain nombre d'unités pratiques prédéfinies à utiliser pour cela :

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

Nous avons également utilisé une conditionnelle ici pour faire varier le nombre de colonnes de notre grille selon l'écran que nous utilisons. Si nous utilisons un écran large (c'est-à-dire un écran d'ordinateur ou un téléphone en mode paysage), la grille aura 4 colonnes, sinon elle en aura 2.

Composants intérieurs

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")
    }
}

 !

  • À gauche, « Kirigami.Heading » : utilise la « date » de l'« ListElement » comme un titre de niveau 1.
  • Au milieu, « ColumnLayout » : possède un objet « Kirigami.Heading » affichant le nom de la tâche, un objet « Kirigami.Separator » fournissant la ligne horizontale et un objet « Controls.Label » affichant la description optionnelle d'une tâche. Les deux derniers composants possèdent une propriété « visible », vérifiant si la description est vide ou non et affiche les composants en fonction du résultat de la condition « description.length > 0 ».
  • A droite, « Controls.Button » : un bouton qui fera quelque chose... bientôt !

Notre application jusqu'à présent

 !

Ainsi, ceci est notre carte basique !

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