Adicionando um diálogo
Tornando nosso aplicativo útil
Temos uma janela, temos cartões e temos ações. No entanto, ainda precisamos encontrar uma maneira de inserir um nome, uma descrição e uma data de nossa escolha.
Uma maneira de fazer isso é criar uma nova página onde colocamos os elementos de entrada necessários. No entanto, uma página inteira dedicada a fornecer nome, descrição e data parece um pouco excessiva.
Em vez disso, usaremos um diálogo.
Abrindo o diálogo
pageStack.initialPage: Kirigami.ScrollablePage {
// ...
actions: [
Kirigami.Action {
id: addAction
icon.name: "list-add"
text: i18nc("@action:button", "Add kountdown")
onTriggered: addDialog.open()
}
]
}
Primeiro, editamos a ação do tutorial anterior: apenas uma Kirigami.Action que aciona a função open() da caixa de diálogo.
Caixas de diálogo de adição de contagem regressiva
O novo componente que adicionamos é um Kirigami.Dialog. As caixas de diálogo aparecem no centro da janela e podem ser usadas para fornecer informações extras relevantes ao conteúdo atual. Elas não podem ser movidas, mas adaptam seu próprio tamanho à janela.
Kirigami.ApplicationWindow {
// ...
Kirigami.Dialog {
id: addDialog
title: i18nc("@title:window", "Add kountdown")
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
padding: Kirigami.Units.largeSpacing
preferredWidth: Kirigami.Units.gridUnit * 20
// Os layouts de formulário ajudam a alinhar e estruturar um layout com várias entradas
Kirigami.FormLayout {
// Os Textfields permitem que você insira texto em uma textbox fina
Controls.TextField {
id: nameField
// Fornece um rótulo anexado ao textfield
Kirigami.FormData.label: i18nc("@label:textbox", "Name*:")
// O que fazer após a entrada ser aceita (por exemplo, pressionar Enter). Neste
// caso, ele move o foco para o próximo campo
onAccepted: descriptionField.forceActiveFocus()
}
Controls.TextField {
id: descriptionField
Kirigami.FormData.label: i18nc("@label:textbox", "Description:")
placeholderText: i18n("Optional")
// Novamente, ele move o foco para o próximo campo
onAccepted: dateField.forceActiveFocus()
}
Controls.TextField {
id: dateField
Kirigami.FormData.label: i18nc("@label:textbox", "ISO Date*:")
// D significa um número obrigatório entre 1 e 9, 9 significa um número obrigatório
// entre 0 e 9
inputMask: "D999-99-99"
// Aqui confirmamos a operação como se estivéssemos clicando no botão OK
onAccepted: addDialog.onAccepted()
}
Controls.Label {
text: "* = required fields"
}
}
// A lógica do diálogo vai aqui
}
// ...
}
Por padrão, as caixas de diálogo têm um [cabeçalho](https://doc.qt.io/qt-6/qml-qtquick-controls -dialog.html#header-prop) e um [rodapé](https://doc.qt.io/qt-6/qml-qtquick -controls-dialog.html#footer-prop), ambos herdados de Controls.Dialog.
O cabeçalho, por padrão, inclui um título e um botão de fechar que pode ser desabilitado com showCloseButton. O rodapé, por padrão, inclui um botão de fechar, que pode ser substituído por standardButtons.
Primeiro, configuramos para exibir um botão "Ok" e um botão "Cancelar", adicionamos algum preenchimento e uma largura de preferência razoável (preferredWidth)(https://api-staging.kde.org/qml-org-kde-kirigami-dialogs-dialog.html#preferredWidth-prop). A largura de preferência é o tamanho padrão esperado da caixa de diálogo, que pode ser aumentado se necessário. Podemos usar o Kirigami.Units padrão, que revisaremos mais tarde.
Em seguida, chegamos a um Kirigami.FormLayout. Ao contrário de um ColumnLayout, o layout de seus componentes filhos é automático e centralizado, com rótulos opcionais. Como o nome indica, ele é usado para criar formulários de entrada.
Esses layouts de formulário foram projetados para funcionar com uma variedade de tipos de entrada diferentes, embora estejamos nos atendo a entradas simples Controls.Textfield que nos fornecem caixas de texto simples para escrever coisas.
Criamos elementos Textfield que atuam como:
- Entrada para o nome da nossa contagem regressiva
- Entrada para a descrição da nossa contagem regressiva
- Entrada para a data para a qual estamos fazendo a contagem regressiva, que deve ser fornecida no formato
AAAA-MM-DD
Dentro de cada um desses elementos Controls.Textfield, estamos definindo uma propriedade Kirigami.FormData.label que nos permite definir rótulos para eles. O formulário apresentará os rótulos corretos à esquerda de cada um desses campos de entrada de texto.
Por fim, também estamos definindo a propriedade onAccepted para acionar o método forceActiveFocus() do campo a seguir; isso alternará o campo ativo assim que o usuário pressionar a tecla ENTER, melhorando a usabilidade do formulário.
Também definimos uma propriedade chamada inputMask no campo de texto para nossa data. Definir como D999-99-99
impede que os usuários insiram algo que possa interromper a funcionalidade do aplicativo (como texto), restringindo-os a inserir apenas dígitos que podemos então tentar analisar em um objeto de data.
Depois que a interface do usuário para o diálogo estiver pronta, precisamos alterar seu comportamento. Para isso, precisamos de três coisas:
- Mostrar o botão Ok somente quando os campos obrigatórios estiverem preenchidos
- Adicionar as informações de entrada ao modelo
- Limpar o formulário de entrada
Kirigami.Dialog {
// ... Após a inicialização do Kirigami.Dialog, queremos criar uma vinculação personalizada para
// tornar o botão Ok visível apenas se os campos de texto obrigatórios estiverem preenchidos.
// Para isso, usamos Kirigami.Dialog.standardButton(button):
Component.onCompleted: {
const button = standardButton(Kirigami.Dialog.Ok);
// () => é uma função de seta JavaScript
button.enabled = Qt.binding( () => requiredFieldsFilled() );
}
onAccepted: {
// A ligação foi criada, mas ainda precisamos torná-la não clicável, a menos que os campos
// sejam preenchidos
if (!addDialog.requiredFieldsFilled()) return;
appendDataToModel();
clearFieldsAndClose();
}
}
A primeira coisa a ser feita é criar uma ligação entre a propriedade enabled do botão OK e uma verificação para verificar se os campos estão preenchidos, o que, neste caso, precisa ser feito com Qt.binding() em JavaScript. Na prática, a linha:
button.enabled = Qt.binding( () => requiredFieldsFilled() );
é semelhante às vinculações QML que vimos até agora, como no seguinte pseudocódigo:
enabled: requiredFieldsFilled()
O manipulador de sinal que aciona o botão Ok é onAccepted. Ele permanece vazio e sem fazer nada se os campos obrigatórios forem preenchidos; caso contrário, ele adicionará a entrada ao modelo e limpará a caixa de diálogo para a próxima vez que for aberta.
Kirigami.Dialog {
// ... Verificamos se o nameField não está vazio e se o dateField (que tem uma inputMask) está
// completamente preenchido
function requiredFieldsFilled() {
return (nameField.text !== "" && dateField.acceptableInput);
}
function appendDataToModel() {
kountdownModel.append({
name: nameField.text,
description: descriptionField.text,
date: new Date(dateField.text)
});
}
function clearFieldsAndClose() {
nameField.text = ""
descriptionField.text = ""
dateField.text = ""
addDialog.close();
}
}
Para o nosso campo de nome obrigatório, tudo o que precisamos fazer é verificar se o campo texto é uma string vazia. Para o campo de data, como ele tem uma máscara de entrada, precisamos usar acceptableInput, que só se torna verdadeiro quando todo o campo estiver preenchido e contiver apenas caracteres aceitáveis.
Em seguida, o método append() do nosso modelo de lista kountdownModel
adiciona um objeto JavaScript incluindo as propriedades que fornecemos.
Por fim, limpamos os campos de texto definindo suas propriedades text como uma string vazia e, em seguida, close().
Depois de salvar nossos arquivos e compilar nosso programa, poderemos adicionar nossas próprias contagens regressivas personalizadas! Podemos dar um último toque para melhorar a interface, ou seja, remover a contagem regressiva fictícia que tínhamos nas lições anteriores:
|
|
Em segundo lugar, agora que temos uma data real para testar, podemos calcular o tempo até a data especificada:
|
|
E em terceiro lugar, aumente o tamanho da janela para que tenhamos mais espaço para nossos novos cartões:
|
|
Muito melhor.
Nosso aplicativo até agora
Main.qml:
|
|