Layouts, ListViews e Cards
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: layout
model: kountdownModel
delegate: kountdownDelegate
}
Isto parece complicado, mas não se preocupe. Vamos começar pelo topo.
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 }
}
Um modelo define a forma como se estrutura um elemento de dados. 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. Os dois primeiros são textos simples, enquanto o terceiro é um número que será usado como substituição.
Nota
Since QML is built on top of JavaScript, many of this language's features are available for use in QML files. However, JavaScript variables have to be prefixed withproperty
in QML.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:
- O ListModelName.append(jsobject objecto) adiciona um objecto em JavaScript fornecido por si ao ListModel, colocando-o a seguir ao último item no modelo. Para isto acontecer de forma correcta, terá de indicar um objecto em JavaScript com as propriedades e tipos de dados correctos e correspondentes.
- ListModelName.get(int indice) devolve o JSObject na posição 'indice' que indicar.
- O ListModelName.remove(int indice, int quantidade) remove o JSObject na posição 'indice' indicada, removendo tantos itens quantos desejar (o valor 1 inclui apenas o JSObject na posição indicada)
- O ListModelName.set(int indice, jsobject objecto) altera o item na posição 'indice' indicada com os valores fornecidos em 'objecto'. Aplicam-se as mesmas regras no
.append
.
Delegado
O objecto delegado lida com a forma como os dados do seu ListModel são apresentados na ListView. Os elementos do tipo CardsListView são desenhados com delegados para cartões em mente, e usámos de facto um elemento Kirigami.AbstractCard
como 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
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'
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
abaixo, para garantir que a GridLayout
não descarta o cartão.
Disposições
O GridLayout
está dentro do componente Item
que fornecemos na 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 em grelha, 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 Kirigami | Pixels |
---|---|
smallSpacing | 4px |
largeSpacing | 8px |
gridUnit | 18px |
Nota
KDE's Visual Design Group (VDG) has a lot more information about different units defined within Plasma and Kirigami on the Human Interface Guidelines site.Também usámos aqui uma condicional para variar o número de colunas na nossa grelha, dependendo do ecrã que estamos a usar. Se estivermos a usar um ecrã panorâmico (i.e., o monitor de um computador ou um telemóvel em modo paisagem), a grelha terá 4 colunas, caso contrário terá 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")
}
}
- À esquerda, o
Kirigami.Heading
: usa odate
doListElement
como cabeçalho de nível 1. - Ao centro, o
ColumnLayout
: tem umKirigami.Heading
que mostra o nome da tarefa; umKirigami.Separator
, que fornece a linha horizontal, e umControls.Label
, que mostra uma descrição opcional para a tarefa. Os dois últimos componentes têm uma propriedadevisible
, que verifica se a descrição está vazia ou não e que mostra os componentes, dependendo do resultado dedescription.length > 0
. - À direita,
Controls.Button
: um botão que irá fazer algo... em breve!
A nossa aplicação até agora
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.