Layouts, ListViews e Cards

Descobrir as diferentes formas de colocar as coisas numa página

Dispor o seu conteúdo

Agora que compreendemos como funcionam as páginas, é a altura de adicionarmos alguma coisa às nossas. Iremos percorrer um conjunto de componentes de disposição importantes e pelos elementos que serão úteis ao desenhar a nossa aplicação.

Não tenha medo dos grandes blocos de código! Iremos percorrer tudo o que ainda não vimos antes e, no fim desta secção, irá ter uma aplicação com bom aspecto.

ListViews

Se já tiver usado o Discover, o NeoChat ou a Configuração do Sistema do Plasma, já terá passado por uma ListView. De uma forma muito simples, as ListViews permitem-lhe mostrar os dados numa lista.

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

Isto parece complicado, mas não se preocupe. Vamos começar pelo topo.

Iremos adicionar este componente dentro da nossa Kirigami.ScrollablePage do último tutorial.

A primeira coisa que irá reparar é que estamos a usar o Kirigami.CardsListView . Esta é uma ListView que nos permite apresentar facilmente cartões numa lista. Contudo, as ListView's são feitas para mostrar dados de um dado modelo - para se preencher a si própria com base num conjunto de dados para onde estamos a apontar. Aí é onde surge a propriedade model: neste exemplo, está a apontar para kountdownModel.

Modelo

ListModel {
    id: kountdownModel
    // Cada ListElement é um elemento na lista, contendo informações
    ListElement {
        name: "Dog birthday!!"
        description: "Big doggo birthday blowout."
        date: 100
    }
}

Iremos adicionar o nosso kountdownModel dentro da nossa Kirigami.ApplicationWindow do último tutorial.

Um modelo define a forma como se estrutura um elemento de dados. O nosso kountdownModel irá consistir apenas num elemento para já. Ao olhar para o nosso ListElement acima, podemos ver como são estruturados os elementos do 'kountdownModel': contêm um nome, uma descrição e uma data. Isto não está 'escrito na pedra', e poderá ter vários tipos de dados no seu modelo. Os dois primeiros são textos simples, enquanto o terceiro é um número que será usado como substituição.

Os modelos são também úteis na forma como poderão ser modificados através do uso de vários métodos. Alguns dos importantes são:

Delegado

Embora o nosso kountdownModel contenha os dados que serão apresentados, o nosso kountdownDelegate irá lidar com a forma como os dados serão apresentados na ListView. o Kirigami.CardsListView foi desenhado para mostrar delegados do tipo cartão, e usámos de facto um elemento Kirigami.AbstractCard como nosso delegado no excerto acima.

Delega automaticamente as propriedades dos ListElements que tivermos configurado no nosso modelo. Poderemos assim fazer referência às propriedades name, description e date dos nossos ListElement's, como se fossem uma variável normal dentro do nosso objecto-delegado.

Construir o nosso cartão delegado

O Component que irá representar o nosso delegado poderá ser adicionado dentro da nossa Kirigami.ApplicationWindow . Iremos então prosseguir, verificando o que cada parte do nosso componente delegado faz.

Component {
    id: kountdownDelegate
    Kirigami.AbstractCard {
        contentItem: Item {
            // O 'implicitWidth/Height' define a largura/altura natural de um item, caso não tenham
            // sido definidas a largura ou a altura. A opção abaixo define o tamanho preferido de um
            // componente com base no seu conteúdo
            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: a fazer... em breve!
                }
            }
        }
    }
}

'implicitWidth' e 'implicitHeight'

A primeira parte que iremos observar é como gerir a largura e a altura do nosso componente:

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

Ao olhar para o nosso Kirigami.AbstractCard , as primeiras propriedades que definimos são a implicitWidth e a implicitHeight. Iremos configurar essas com os valores de delegateLayout.implicitWidth e delegateLayout.implicitHeight, i.e. a implicitWidth e implicitHeight do elemento GridLayout. As largura e alturas implícitas são propriedades que são definidas como uma espécie de valor predefinido, i.e. se não existir nenhuma largura ou altura explícita para estes componentes. Como tal, configurámos a implicitWidth e implicitHeight do nosso Kirigami.AbstractCard com os valores respectivos da GridLayout](https://doc.qt.io/qt-6/qml-qtquick-layouts-gridlayout.html) abaixo, para garantir que a GridLayout não descarta o cartão.

Disposições

O GridLayout está dentro do componente Item que passámos à propriedade contentItem. Este é o Item que contém o que será apresentado no seu cartão.

Também teremos de escolher uma disposição para os nossos componentes, para que eles simplesmente não fiquem empilhados uns em cima dos outros. Existem três tipos principais que poderemos escolher:

  • O ColumnLayout dispõe os seus componentes na vertical, numa única coluna
  • O RowLayout dispõe os seus componentes na horizontal, numa única linha
  • O GridLayout dispõe os seus componentes numa grelha com uma composição à sua escolha

Com o ColumnLayout e o RowLayout, tudo o que temos a fazer é escrever os nossos componentes dentro do componente Layout. Como poderá ver, usámos uma disposição com a GridLayout, que traz consigo um pouco mais de trabalho.

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

A primeira coisa que vê é a nossa anchor. O sistema de associação de elementos do QtQuick oferece uma forma útil de garantir que os seus componentes estão posicionados em cartas partes de um dado componente-pai. Nós associámos a nossa GridLayout aos extremos esquerdo, superior e direito no cartão-pai, garantindo que o nosso conteúdo se prolonga ao longo do cartão completo.

Em seguida definimos o espaço entre as linhas e colunas dentro da nossa grelha, para que os nossos componentes não se amontoem. O Kirigami oferece um conjunto de unidades predefinidas a usar para este fim:

Unidade do KirigamiPixels
smallSpacing4px
largeSpacing8px
gridUnit18px

Como se poderá recordar, o root é o ID da nossa Kirigami.ApplicationWindow . Oferece a propriedade wideScreen , usada para determinar se o dispositivo actual tem um ecrã panorâmico (i.e., um monitor de computador ou um telefone no modo paisagem). Usamos uma condição ternária aqui para variar o número de colunas na nossa grelha, dependendo do ecrã que estamos a usar: se for um ecrã panorâmico, a grelha terá 4 colunas, caso contrário terás 2.

Componentes interiores

Poderemos criar apenas três legendas dentro do nosso componente delegado e seria tudo. Mas não ficaria propriamente bonito.

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

Imagem a mostrar como é a aparência de um Card

  • À esquerda, o Kirigami.Heading](docs:kirigami2;Heading): usa o date do ListElement como cabeçalho de nível 1.
  • Ao centro, o ColumnLayout: tem um Kirigami.Heading que mostra o nome da tarefa; um Kirigami.Separator , que fornece a linha horizontal, e um Controls.Label , que mostra uma descrição opcional para a tarefa. Os dois últimos componentes têm uma propriedade visible, que verifica se a descrição está vazia ou não e que mostra os componentes, dependendo do resultado de description.length > 0.
  • À direita, Controls.Button : um botão que irá fazer algo... em breve!

A nossa aplicação até agora

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

Imagem da aparência da aplicação depois de terminar esta lição

Aqui temos então o nosso cartão básico!

Com estes passos, já dispomos o trabalho de base para adicionar todas as funcionalidades à nossa aplicação.