Добавление диалога
Делаем приложение полезным
Окно есть, карточки есть, действия есть. Однако по-прежнему нужно найти способ ввода имени, описания и даты по своему выбору.
Один из способов сделать это — создать новую страницу, на которой размещаются необходимые элементы ввода. Однако целая страница, отведённая под ввод имени, описания и даты, кажется несколько избыточной.
Вместо этого будет использоваться диалог.

Открытие диалога
pageStack.initialPage: Kirigami.ScrollablePage {
// …
actions: [
Kirigami.Action {
id: addAction
icon.name: "list-add"
text: i18nc("@action:button", "Add kountdown")
onTriggered: addDialog.open()
}
]
}Сначала отредактируем действие из предыдущего руководства: это просто Kirigami.Action, вызывающее функцию open() диалога.
Диалог добавления обратных отсчётов
Добавляемый компонент — это Kirigami.Dialog. Диалоги появляются в центре окна и позволяют предоставить дополнительную информацию, относящуюся к текущему содержимому. Переместить их нельзя, но они подстраивают свой размер под окно.
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
// Компоновки форм помогают выравнивать и структурировать компоновку с несколькими полями
// ввода
Kirigami.FormLayout {
// Текстовые поля позволяют вводить текст в узком поле ввода
Controls.TextField {
id: nameField
// Добавляет надпись, привязанную к текстовому полю
Kirigami.FormData.label: i18nc("@label:textbox", "Name*:")
// Что делать после подтверждения ввода (то есть нажатия клавиши Enter). В этом
// случае фокус перемещается на следующее поле.
onAccepted: descriptionField.forceActiveFocus()
}
Controls.TextField {
id: descriptionField
Kirigami.FormData.label: i18nc("@label:textbox", "Description:")
placeholderText: i18n("Optional")
// Снова фокус перемещается на следующее поле
onAccepted: dateField.forceActiveFocus()
}
Controls.TextField {
id: dateField
Kirigami.FormData.label: i18nc("@label:textbox", "ISO Date*:")
// D означает обязательную цифру от 1 до 9, 9 означает обязательную цифру от 0 до 9
inputMask: "D999-99-99"
// Здесь операция подтверждается так же, как при нажатии кнопки «ОК»
onAccepted: addDialog.accepted()
}
Controls.Label {
text: "* = required fields"
}
}
// Здесь размещается логика диалога
}
// …
}По умолчанию диалоги имеют header и footer, оба унаследованы от Controls.Dialog.
Заголовок по умолчанию содержит title и кнопку закрытия, которую можно отключить с помощью showCloseButton. Нижний колонтитул по умолчанию не содержит кнопок. Её можно переопределить с помощью standardButtons.
Сначала задаётся показ кнопок «ОК» и «Отмена», добавляются отступы и разумное значение preferredWidth. Предпочитаемая ширина — это ожидаемый размер диалога по умолчанию, который при необходимости может увеличиваться. Можно использовать стандартные Kirigami.Units, которые будут рассмотрены позже.
Далее следует Kirigami.FormLayout. В отличие от ColumnLayout, расположение его дочерних компонентов задаётся автоматически и по центру, а подписи необязательны. Как следует из названия, он предназначен для создания форм ввода.
Эти компоновки форм предназначены для работы с различными типами ввода, однако здесь используются простые поля Controls.Textfield, предоставляющие простые текстовые поля для ввода.
Созданы элементы Textfield, которые выполняют роль:
- Поле ввода названия обратного отсчёта
- Поле ввода описания обратного отсчёта
- Поле ввода даты, до которой ведётся обратный отсчёт; дата должна быть указана в формате
YYYY-MM-DD
В каждом из этих элементов Controls.Textfield задаётся свойство Kirigami.FormData.label, позволяющее определить для них надписи. Форма покажет правильные надписи слева от каждого из этих полей ввода текста.
Наконец, свойство onAccepted также задаётся для вызова метода forceActiveFocus() следующего поля; это переключит активное поле, когда пользователь нажмёт клавишу ENTER, что повышает удобство работы с формой.
Также для текстового поля даты задано свойство inputMask. Задание этому свойству значения D999-99-99 не позволяет пользователям вводить что-либо, что может нарушить работу приложения (например, текст), ограничивая ввод только цифрами, которые затем можно попытаться преобразовать в объект даты.
Когда пользовательский интерфейс диалога готов, необходимо изменить его поведение. Для этого потребуется три вещи:
- Показывать кнопку «ОК» только тогда, когда заполнены обязательные поля
- Добавлять вводимую информацию в модель
- Очищать форму ввода
Kirigami.Dialog {
// ... После инициализации Kirigami.Dialog требуется создать пользовательскую привязку, чтобы
// кнопка «ОК» показывалась только при заполнении обязательных текстовых полей. Для этого
// используется Kirigami.Dialog.standardButton(button):
Component.onCompleted: {
const button = standardButton(Kirigami.Dialog.Ok);
// () => — это стрелочная функция JavaScript
button.enabled = Qt.binding( () => requiredFieldsFilled() );
}
onAccepted: {
// Привязка создана, но ещё требуется сделать кнопку недоступной для нажатия, пока поля не
// заполнены
if (!addDialog.requiredFieldsFilled()) return;
appendDataToModel();
clearFieldsAndClose();
}
}Первое, что нужно сделать, — создать привязку между свойством enabled кнопки «ОК» и проверкой того, заполнены ли поля; в этом случае привязку нужно создать с помощью Qt.binding() в JavaScript. По сути, строка:
button.enabled = Qt.binding( () => requiredFieldsFilled() );аналогично привязкам QML, рассмотренным ранее, например в следующем псевдокоде:
enabled: requiredFieldsFilled()Обработчик сигнала, срабатывающий при нажатии кнопки «ОК», — это onAccepted. Если обязательные поля заполнены, он остаётся пустым и ничего не делает; в противном случае он добавит введённые данные в модель и очистит диалог при следующем открытии.
Kirigami.Dialog {
// … Проверяется, что поле nameField не пустое и что поле dateField (для которого задана маска
// ввода) заполнено полностью
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();
}
}Для обязательного поля имени достаточно проверить, является ли текст поля пустой строкой. Для поля даты, поскольку у него есть маска ввода, вместо этого нужно использовать acceptableInput, которое становится истинным только после того, как поле заполнено целиком и содержит только допустимые символы.
Затем метод append() модели списка kountdownModel добавляет объект JavaScript, содержащий предоставленные свойства.
Наконец, текстовые поля очищаются присвоением их свойствам text пустой строки, после чего вызывается close().
После сохранения файлов и сборки приложения можно будет добавить собственные обратные отсчёты! Осталось внести последний штрих для улучшения интерфейса — удалить фиктивный обратный отсчёт из предыдущих руководств:
| |
Во-вторых, теперь, когда есть настоящая дата, с которой можно работать, можно вычислить время до этой даты:
| |
И в-третьих, увеличьте размер окна, чтобы было больше места для новых карточек:
| |
Гораздо лучше.
Наше приложение на данный момент
Main.qml:
| |
