Диалоги
Интерфейс Dialogs разработан для отображения стандартных диалоговых окон. Диалоговое окно - это небольшое всплывающее окно, которое можно использовать для представления информации и элементов пользовательского интерфейса.
Методы createMessageDialog(), createOptionDialog() и createInputDialog() - это точки входа в fluent API, который позволяет создавать и отображать диалоговые окна.
Диалог сообщения
Диалог сообщения отображает сообщение пользователю.
В следующем примере диалог сообщения отображается при нажатии кнопки пользователем:
@Autowired
private Dialogs dialogs;
@Subscribe("messageDialogButton")
public void onHelloButtonClick(ClickEvent<Button> event) {
dialogs.createMessageDialog()
.withHeader("Success") (1)
.withText("Invitation sent successfully") (2)
.open();
}
| 1 | Добавляет заголовок диалога. |
| 2 | Добавляет текстовое сообщение для отображения в диалоге. |
Следующие методы позволяют вам настроить внешний вид и поведение диалога сообщения:
withHeader - withWidth - withHeight - withLeft - withTop - withText - withContent - withModal - withThemeName - withClassName - withDraggable - withResizable - withMinWidth - withMaxWidth - withMaxWidth - withMaxHeight - withCloseOnOutsideClick - withCloseOnEsc - withDraggedListener - withResizeListener
Диалог с вариантами
Диалог с вариантами отображает сообщение и набор кнопок для выбора пользователем.
Используйте метод withActions() для предоставления действий, каждое из которых представлено кнопкой в диалоге. Например:
@Autowired
private Dialogs dialogs;
@Subscribe("selectOptionButton")
public void onSelectOptionButtonClick(ClickEvent<Button> event) {
dialogs.createOptionDialog()
.withHeader("Please confirm")
.withText("Do you really want to add a customer?")
.withActions(
new DialogAction(DialogAction.Type.YES)
.withHandler(e -> addCustomer()), (1)
new DialogAction(DialogAction.Type.NO)
)
.open();
}
| 1 | Если пользователь нажмет "Yes", диалог закроется и вызовется метод addCustomer(). |
Базовый класс DialogAction предназначен для создания действий со стандартными именами и значками. Поддерживаются пять типов действий, определенных перечислением DialogAction.Type: OK, CANCEL, YES, NO, CLOSE. Названия кнопок извлекаются из пакета сообщений.
Следующие методы позволяют вам настроить внешний вид и поведение диалога с вариантами:
Диалог фоновой задачи
Диалог фоновой задачи обеспечивает удобный для пользователя опыт для длительных задач, где пользователь может отслеживать прогресс и отменять операцию при необходимости.
| Для получения дополнительной информации о механизме фоновых задач, посетите статью Фоновые Задачи. |
Чтобы настроить диалог, передайте в него объект фоновой задачи:
@Autowired
private Dialogs dialogs;
@Subscribe(id = "backgroundTaskButton", subject = "singleClickListener")
public void onBackgroundTaskClick(final ClickEvent<JmixButton> event) {
dialogs.createBackgroundTaskDialog(new SampleTask(15, this, 10)) (1)
.withHeader("Background task running")
.withText("Please wait until the task is complete")
.withTotal(10) (2)
.withCancelAllowed(true) (3)
.open();
}
protected class SampleTask extends BackgroundTask<Integer, Void> {
int count;
public SampleTask(long timeoutSeconds, View<?> view, int count) {
super(timeoutSeconds, view);
this.count = count;
}
@Override
public Void run(TaskLifeCycle<Integer> taskLifeCycle) throws Exception {
for (int i = 1; i < count + 1; i++) {
Thread.sleep(1000);
taskLifeCycle.publish(i);
}
return null;
}
}
| 1 | Передайте объект фоновой задачи в диалог. |
| 2 | Установите количество секций в индикаторе прогресса в соответствии с количеством задач. |
| 3 | Разрешить пользователю прерывать задачу. |
Следующие методы позволяют вам настроить внешний вид и поведение диалога фоновой задачи:
Диалог ввода
Диалог ввода - универсальный инструмент, который позволяет создавать формы ввода с использованием API и часто позволяет избежать создания экранов для ввода тривиальных данных. Он позволяет вводить значения разных типов, выполнять проверку ввода и предоставлять различные действия, доступные для выбора пользователем.
Следующие методы позволяют вам настроить внешний вид и поведение диалога ввода:
withHeader - withWidth - withHeight - withLeft - withTop - withText - withDraggable - withDraggedListener
Рассмотрим несколько примеров.
Стандартные параметры
Используйте метод withParameters() для добавления параметров, каждый из которых будет представлен полем ввода в диалоге.
Следующий пример создает диалоговое окно ввода с параметрами стандартных типов и стандартными действиями OK/Cancel:
@Autowired
private Dialogs dialogs;
@Subscribe("standardParametersButton")
public void onStandardParametersButtonClick(ClickEvent<Button> event) {
dialogs.createInputDialog(this)
.withHeader("Enter values")
.withParameters(
stringParameter("name").withLabel("Name").withRequired(true), (1)
intParameter("amount").withLabel("Amount").withDefaultValue(1), (2)
entityParameter("user", User.class).withLabel("User"), (3)
enumParameter("status", OnboardingStatus.class).withLabel("Status") (4)
)
.withActions(DialogActions.OK_CANCEL) (5)
.withCloseListener(closeEvent -> {
if (closeEvent.closedWith(DialogOutcome.OK)) { (6)
String name = closeEvent.getValue("name"); (7)
int amount = closeEvent.getValue("amount");
User user = closeEvent.getValue("user");
OnboardingStatus status = closeEvent.getValue("status");
// process entered values...
}
})
.open();
}
| 1 | Указывает обязательный строковый параметр. |
| 2 | Указывает целочисленный параметр с значением по умолчанию. |
| 3 | Указывает параметр-сущность. |
| 4 | Указывает параметр-перечисление. |
| 5 | Указывает стандартные действия OK/Cancel, представленные кнопками внизу диалога. |
| 6 | В обработчике закрытия можно проверить, какое действие выбрал пользователь. |
| 7 | Событие закрытия содержит введенные значения, которые можно получить с использованием идентификаторов параметров. |
Пользовательские параметры
Следующий пример иллюстрирует создание пользовательского параметра, который позволяет пользователю выбрать значение из выпадающего списка:
@Autowired
private Dialogs dialogs;
@Autowired
private DataManager dataManager;
@Autowired
private UiComponents uiComponents;
@Subscribe("customParameterButton")
public void onCustomParameterButtonClick(ClickEvent<Button> event) {
dialogs.createInputDialog(this)
.withHeader("Enter values")
.withParameters(
stringParameter("name").withLabel("Name").withRequired(true),
intParameter("amount").withLabel("Amount").withDefaultValue(1),
parameter("user") (1)
.withLabel("User")
.withField(() -> {
EntityComboBox<User> field = uiComponents.create(EntityComboBox.class); (2)
field.setItems(dataManager.load(User.class).all().list()); (3)
field.setWidthFull();
return field;
}),
enumParameter("status", OnboardingStatus.class).withLabel("Status")
)
.withActions(DialogActions.OK_CANCEL).withCloseListener(closeEvent -> {
if (closeEvent.closedWith(DialogOutcome.OK)) {
String name = closeEvent.getValue("name");
int amount = closeEvent.getValue("amount");
User user = closeEvent.getValue("user");
OnboardingStatus status = closeEvent.getValue("status");
// process entered values...
}
})
.open();
}
| 1 | Указывает пользовательский параметр. |
| 2 | Создает выпадающий список внутри поля пользовательского параметра. |
| 3 | Загружает список вариантов в выпадающий список. |
|
При использовании метода Следующие атрибуты
Это связано с тем, что вы полностью отвечаете за ручное создание и настройку поля внутри лямбда-выражения. Поэтому все дополнительные настройки, кроме В приведённом ниже примере важные атрибуты, такие как
Если вам не нужно вручную настраивать поле, вы можете просто задать атрибуты напрямую в
В этом случае Jmix автоматически создаст поле и применит все указанные атрибуты. |
Пользовательский валидатор
Диалог ввода поддерживает базовую проверку: он может проверять тип введенных значений и что обязательное поле не пусто. Кроме того, вы можете добавить более общие пользовательские валидаторы.
Следующий пример добавляет валидатор, проверяющий, что хотя бы один параметр введен:
@Autowired
private Dialogs dialogs;
@Subscribe("validationButton")
public void onValidationButtonClick(ClickEvent<Button> event) {
dialogs.createInputDialog(this)
.withHeader("Enter at least one value")
.withParameters(
stringParameter("name").withLabel("Name").withRequired(true),
entityParameter("User", User.class).withLabel("User")
)
.withValidator(context -> { (1)
String name = context.getValue("name"); (2)
User user = context.getValue("user");
if (Strings.isNullOrEmpty(name) && user == null) {
return ValidationErrors.of("Enter name or select a customer"); (3)
}
return ValidationErrors.none();
})
.withActions(DialogActions.OK_CANCEL)
.withCloseListener(closeEvent -> {
if (closeEvent.closedWith(DialogOutcome.OK)) {
String name = closeEvent.getValue("name");
User user = closeEvent.getValue("user");
// process entered values...
}
})
.open();
}
| 1 | Пользовательский валидатор, добавляющий логику для обеспечения ввода хотя бы одного параметра. |
| 2 | В валидаторе можно получить значения параметров из объекта контекста. |
| 3 | Валидатор возвращает ошибки валидации, если параметры не введены. |
Side Dialog
Диалог, который появляется с одной из сторон экрана.
Диалог можно создать с помощью бина Dialogs:
@Autowired
private Dialogs dialogs;
@ViewComponent
private MessageBundle messageBundle;
@Subscribe(id = "sideDialogButton", subject = "clickListener")
public void onSimpleSideDialogButtonClick(final ClickEvent<JmixButton> event) {
dialogs.createSideDialog()
.withHorizontalSize("14em")
.withHeaderProvider(this::createHeader)
.withContentComponents(createContent())
.open();
}
private HorizontalLayout createHeader(SideDialog sideDialog) {
HorizontalLayout header = new HorizontalLayout();
header.setWidthFull();
header.add(new H2(messageBundle.getMessage("sideDialogHeader")));
header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
Button closeButton = new Button(LumoIcon.CROSS.create(), event -> sideDialog.close());
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
header.add(closeButton);
return header;
}
private Component createContent() {
VirtualList<String> list = new VirtualList<>();
list.setWidthFull();
list.setItems("Item 1", "Item 2", "Item 3");
list.setRenderer(new ComponentRenderer<>((item -> {
Card root = new Card();
root.setTitle(item);
root.setHeaderSuffix(LumoIcon.CROSS.create());
root.add(messageBundle.getMessage("activityDescription"));
root.addThemeVariants(CardVariant.LUMO_HORIZONTAL);
root.addClassName(LumoUtility.Margin.Bottom.MEDIUM);
return root;
})));
return list;
}
Расположение и размер
Используйте withSideDialogPosition(), чтобы задать сторону появления диалога. Возможные значения: RIGHT, TOP, BOTTOM, LEFT, INLINE_START, INLINE_END.
INLINE_START и INLINE_END учитывают направление чтения экрана (LTR/RTL).
Размер диалога настраивается по-разному в зависимости от его ориентации:
Для горизонтальных диалогов (LEFT, RIGHT, INLINE_START, INLINE_END):
-
setHorizontalSize() -
setHorizontalMaxSize() -
setHorizontalMinSize()
Для вертикальных диалогов (TOP, BOTTOM):
-
setVerticalSize() -
setVerticalMaxSize() -
setVerticalMinSize()
Содержимое
Содержимое диалога определяется через провайдеры:
Метод |
Описание |
|---|---|
|
Задаёт компоненты для основной области диалога. |
|
Задаёт пользовательские компоненты для заголовка. |
|
Задаёт пользовательские компоненты для футера. |
События
Доступны следующие события:
Событие |
Описание |
|---|---|
|
Срабатывает при открытии или закрытии диалога. |
|
Срабатывает при нажатии |
Стилизация
Внешний вид и размеры диалога можно настроить с помощью CSS.
Размеры
Размер диалога можно задать глобально через CSS-переменные. Эти значения применяются ко всем экземплярам SideDialog.
| Если размеры заданы программно через Java API, они имеют приоритет над CSS. |
Горизонтальные размеры (для LEFT, RIGHT, INLINE_START, INLINE_END):
Переменная |
Описание |
По умолчанию |
|---|---|---|
|
Базовая ширина диалога. |
|
|
Максимальная ширина. |
|
|
Минимальная ширина. |
|
Вертикальные размеры (для TOP, BOTTOM):
Переменная |
Описание |
По умолчанию |
|---|---|---|
|
Базовая высота диалога. |
|
|
Максимальная высота. |
|
|
Минимальная высота. |
|
Внешний вид
Переменная |
Описание |
По умолчанию |
|---|---|---|
|
Длительность анимации открытия и закрытия. |
|
Части
Side dialog включает следующие стилизируемые части:
Часть |
Описание |
|---|---|
|
Модальный фон (затемнение за диалогом). |
|
Основной контейнер диалога. |
|
Заголовок диалога. |
|
Обёртка заголовка. |
|
Элемент заголовка. |
|
Контейнер пользовательского заголовка. |
|
Основная область содержимого. |
|
Нижняя часть диалога. |
Состояния
Side dialog поддерживает следующие состояния:
Состояние |
Описание |
|---|---|
|
Применяется в зависимости от позиции диалога (например, |
|
Применяется, когда диалог отображается в полноэкранном режиме |
Настройка диалогов
Вы можете настроить заголовок, размер и положение всех диалоговых окон, используя следующие методы:
-
withHeader()- устанавливает текст заголовка.
-
withWidth()- устанавливает ширину диалогового окна.
-
withHeight()- устанавливает высоту диалогового окна.
-
withLeft()- устанавливает отступ диалогового окна слева от его контейнера (значения без единиц измерения интерпретируются как пиксели).
-
withTop()- устанавливает отступ диалогового окна сверху (значения без единиц измерения интерпретируются как пиксели).
Например:
@Subscribe(id = "configDialogButton", subject = "clickListener")
public void onConfigDialogButtonClick(final ClickEvent<JmixButton> event) {
dialogs.createMessageDialog()
.withHeader("Information")
.withWidth("600px")
.withHeight("200px")
.withTop("100px")
.open();
}
Дополнительные настройки внешнего вида и поведения доступны для определенных типов диалоговых окон:
-
withText()- устанавливает текстовое сообщение, отображаемое в диалоговом окне.
-
withContent()- устанавливает содержимое диалогового окна. Это текст, отображаемый внутри компонентаParagraph. Обратите внимание, что это переопределяет любой текст, ранее установленный с помощьюwithText().Форматирование текста доступно с использованием HTML - диалоговое окно может отображать HTML-контент с помощью метода
withContent(). Заданный HTML-фрагмент должен быть заключен в компонент:@Autowired private Dialogs dialogs; Html htmlContent = new Html("<p>Here starts a paragraph. A new line starts after this.<br />" + "<b>This text is bold.</b> <i>This text is italic.</i></p>"); @Subscribe("htmlContentButton") public void onHtmlContentButtonClick(ClickEvent<Button> event) { dialogs.createMessageDialog() .withHeader("HTML Formatting") .withContent(htmlContent) .open(); }
-
withModal()- если установлено значениеfalse, отображает диалоговое окно как немодальное, позволяя взаимодействовать с другими компонентами приложения. Диалоги по умолчанию модальные.
-
withThemeName()- устанавливает имя (или имена) темы диалога, заменяя любые ранее установленные значения.
-
withClassName()- устанавливает имя (или имена) CSS-класса для компонента, заменяя все ранее определенные классы.
-
withDraggable()- определяет, может ли пользователь перетаскивать диалог. Диалоги можно перетаскивать по умолчанию.
-
withResizable()- определяет, может ли пользователь изменять размер диалога. По умолчанию размер диалога изменить нельзя.
-
withMinWidth(),withMaxWidth(),withMaxWidth(),withMaxHeight()- устанавливают минимальную/максимальную ширину и высоту диалога, соответственно.
-
withCloseOnOutsideClick()- управляет тем, закрывается ли диалог при щелчке за его пределами. По умолчанию диалог закрывается при щелчке снаружи.
-
withCloseOnEsc()- управляет тем, закрывается ли диалог при нажатии клавишиESC. По умолчанию диалог закрывается клавишейESC.
-
withDraggedListener()- регистрирует callback, вызываемый после завершения перетаскивания пользователем. Вызывается только в том случае, если перетаскивание включено.По умолчанию компонент синхронизирует значения top/left после каждого перетаскивания. Например:
@Subscribe(id = "dragDialogButton", subject = "clickListener") public void onDragDialogButtonClick(final ClickEvent<JmixButton> event) { dialogs.createMessageDialog() .withHeader("Drag this dialog") .withDraggedListener(dialogDraggedEvent -> { String left = dialogDraggedEvent.getLeft(); String top = dialogDraggedEvent.getTop(); try { int leftValue = Integer.parseInt(left.replace("px", "")); int topValue = Integer.parseInt(top.replace("px", "")); if (leftValue < 300 && topValue < 200) { notifications.create("Dialog is in the upper left corner").show(); } else if (leftValue > 800 && topValue > 500) { notifications.create("Dialog is in the lower right corner").show(); } else { notifications.create("Dialog is in a neutral area").show(); } } catch (NumberFormatException e) { notifications.create("Error: Invalid coordinates") .withType(Notifications.Type.WARNING) .show(); } }) .open(); }
-
withResizeListener()- регистрирует callback, вызываемый после завершения изменения размера пользователем. Вызывается только в том случае, если изменение размера включено.По умолчанию компонент синхронизирует значения ширины/высоты и верхнего/левого отступов после каждого изменения размера. Например:
@Subscribe(id = "resizeDialogButton", subject = "clickListener") public void onResizeDialogButtonClick(final ClickEvent<JmixButton> event) { dialogs.createMessageDialog() .withHeader("Resize this dialog") .withResizable(true) .withResizeListener(dialogResizeEvent -> { String width = dialogResizeEvent.getWidth(); String height = dialogResizeEvent.getHeight(); try { int widthValue = Integer.parseInt(width); int heightValue = Integer.parseInt(height); if (widthValue < 400 || heightValue < 300) { notifications.create("Minimum size: 400×300") .withType(Notifications.Type.WARNING) .show(); } } catch (NumberFormatException e) { notifications.create("Error: Invalid coordinates") .withType(Notifications.Type.WARNING) .show(); } }) .open(); }