Ajouter une boîte de dialogue
Rendre notre application utile
Nous avons une fenêtre, nous avons des cartes et nous avons des actions. Pourtant, nous devons encore trouver un moyen de saisir un nom, une description et une date de notre choix.
Une façon d'y parvenir est de créer une nouvelle page où nous plaçons les éléments d'entrée requis. Cependant, une page entière dédiée à la fourniture d'un nom, une description et une date semble un peu excessive.
A la place, nous allons utiliser une boîte de dialogue.
Ouverture de la boîte de dialogue
pageStack.initialPage: Kirigami.ScrollablePage {
// ...
actions: [
Kirigami.Action {
id: addAction
icon.name: "list-add"
text: i18nc("@action:button", "Add kountdown")
onTriggered: addDialog.open()
}
]
}
First we edit the action from the previous tutorial: just a Kirigami.Action that triggers the dialog's open() function.
Boîtes de dialogue ajoutant un compte à rebours
The new component we add is a Kirigami.Dialog. Dialogs appear at the center of the window and can be used to provide extra information relevant to the current content. They can't be moved, but they adapt their own size to the window.
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
// Les dispositions de formulaire aident à aligner et à structurer une disposition avec
// plusieurs entrées.
Kirigami.FormLayout {
// Les champs de texte vous permettent de saisir du texte dans une zone de texte mince.
Controls.TextField {
id: nameField
// Fournit une étiquette associée au champ de texte
Kirigami.FormData.label: i18nc("@label:textbox", "Name*:")
// Que faire après l'acceptation de la saisie (C'est-à-dire avec un appui sur la
// touche « Entrée »). Dans ce cas, le focus est déplacé vers le champ suivant.
onAccepted: descriptionField.forceActiveFocus()
}
Controls.TextField {
id: descriptionField
Kirigami.FormData.label: i18nc("@label:textbox", "Description:")
placeholderText: i18n("Optional")
// Encore une fois, cela déplace le focus vers le champ suivant
onAccepted: dateField.forceActiveFocus()
}
Controls.TextField {
id: dateField
Kirigami.FormData.label: i18nc("@label:textbox", "ISO Date*:")
// Le paramètre « D » signifie un nombre requis entre 1 et 9. La valeur 9 signifie
// un nombre requis entre 0 et 9.
inputMask: "D999-99-99"
// Ici, nous confirmons l'opération, simplement en cliquant sur le bouton « Ok »
onAccepted: addDialog.onAccepted()
}
Controls.Label {
text: "* = required fields"
}
}
// La logique de la boîte de dialogue va ici
}
// ...
}
Dialogs by default have a header and a footer, both inherited from Controls.Dialog.
The header by default includes a title and a close button that can be disabled with showCloseButton. The footer by default includes a close button, and it can be overridden with standardButtons.
We first set it to show an "Ok" button and a "Cancel" button, add some padding, and add a reasonable preferredWidth. The preferred width is the default expected size of the dialog, which can increase if needed. We can use standard Kirigami.Units that we will revisit later on.
Then we come to a Kirigami.FormLayout. Unlike a ColumnLayout, the layout of its child components is automatic and centered, with optional labels. As the name implies, it is used to create input forms.
These form layouts are designed to work with a variety of different input types, though we're sticking to simple Controls.Textfield inputs that give us simple text boxes to write things in.
Nous avons créé des éléments « Textfield » agissant comme :
- Entrée pour le nom de notre compteur
- Entrée pour la description de notre compteur
- Saisissez la date à partir de laquelle nous effectuons le compte à rebours, qui doit être fournie au format « AAAA-MM-JJ ».
Within each of these Controls.Textfield elements, we are setting a Kirigami.FormData.label property that lets us define labels for them. The form will present the correct labels to the left of each of these text input fields.
Finally, we are also setting the onAccepted property to trigger the forceActiveFocus() method of the following field; this will switch the active field once the user hits the ENTER key, improving the usability of the form.
We have also set a property called inputMask on the text field for our date. Setting this to D999-99-99
prevents users from entering something that might break the functionality of the application (such as text), restricting them to only entering digits which we can then try to parse into a date object.
Once the user interface for the dialog is done, we need to change how it behaves. For this we need three things:
- Afficher le bouton « Ok » uniquement lorsque les champs obligatoires sont remplis.
- Ajouter des informations d'entrées concernant le modèle
- Effacer le formulaire de saisie
Kirigami.Dialog {
// ... Once the Kirigami.Dialog is initialized, we want to create a custom binding to only make
// the Ok button visible if the required text fields are filled. For this we use
// Kirigami.Dialog.standardButton(button):
Component.onCompleted: {
const button = standardButton(Kirigami.Dialog.Ok);
// () => est une fonction de flèche en JavaScript
button.enabled = Qt.binding( () => requiredFieldsFilled() );
}
onAccepted: {
// La liaison est créée, mais nous devons toujours la rendre non cliquable à moins que les
// champs ne soient remplis
if (!addDialog.requiredFieldsFilled()) return;
appendDataToModel();
clearFieldsAndClose();
}
}
The first thing that needs to be done is create a binding between the OK button's enabled property and a check on whether the fields are filled, which in this case needs to be done with Qt.binding() in JavaScript. In effect, the line:
button.enabled = Qt.binding( () => requiredFieldsFilled() );
est similaire aux liaisons QML que nous avons vues jusqu'à présent, comme dans le pseudo-code suivant :
enabled: requiredFieldsFilled()
The signal handler that triggers the Ok button is onAccepted. It remains empty and without doing anything if the required fields are filled; otherwise, it will add the input to the model and clear the dialog for the next time it is opened.
Kirigami.Dialog {
// ... We check that the nameField is not empty and that the dateField (which has an inputMask)
// is completely filled
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();
}
}
For our required name field, all we need to do is to check whether the field text is an empty string. For the date field, because it has an input mask, we need to use acceptableInput instead, which only becomes true once the whole field is filled and contains only acceptable characters.
Then, the append() method of our kountdownModel
list model adds a JavaScript object including the properties we have provided.
Lastly, we make sure to clear the text fields by setting their text properties to an empty string, then close() it.
Once we save our files and build our program, we'll be able to add our own custom countdowns! We can make one last touch to improve the interface, namely remove the dummy countdown we had in the previous lessons:
|
|
Deuxièmement, maintenant que nous avons une date courante avec laquelle jouer, nous pouvons calculer le temps jusqu'à ladite date :
|
|
Et troisièmement, augmenter la taille de la fenêtre afin que nous ayons plus de place pour nos nouvelles cartes :
|
|
Beaucoup plus agréable.
Notre application jusqu'à présent
Main.qml :
|
|
! Capture d'écran de l'application avec quatre exemples de cartes