ConfirmDialog.java
package swingtree.dialogs;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import swingtree.UI;
import swingtree.api.IconDeclaration;
import javax.swing.Icon;
import javax.swing.JOptionPane;
import java.awt.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* An immutable builder class for creating simple confirmation dialogs
* based on the {@link JOptionPane} class, more specifically the
* {@link JOptionPane#showOptionDialog(Component, Object, String, int, int, Icon, Object[], Object)}
* method.
* <p>
* This class is intended to be used as part of the {@link UI} API
* by calling the {@link UI#confirmation(String)} factory method.
*/
public final class ConfirmDialog
{
private static final Logger log = org.slf4j.LoggerFactory.getLogger(ConfirmDialog.class);
/**
* Creates a new {@link ConfirmDialog} instance with the specified question.
* @param question The question to ask the user.
* @return A new {@link ConfirmDialog} instance with the specified question.
*/
public static ConfirmDialog asking(String question ) {
Objects.requireNonNull(question);
return new ConfirmDialog(
-1,
"",
question,
"Yes",
"No",
"Cancel",
ConfirmAnswer.YES,
null,
null
);
}
private final int _type;
private final String _title;
private final String _message;
private final String _yesOption;
private final String _noOption;
private final String _cancelOption;
private final ConfirmAnswer _defaultOption;
private final @Nullable Icon _icon;
private final @Nullable Component _parent;
private ConfirmDialog(
int type,
String title,
String message,
String yesOption,
String noOption,
String cancelOption,
ConfirmAnswer defaultOption,
@Nullable Icon icon,
@Nullable Component parent
) {
_type = type;
_title = Objects.requireNonNull(title);
_message = Objects.requireNonNull(message);
_yesOption = Objects.requireNonNull(yesOption);
_noOption = Objects.requireNonNull(noOption);
_cancelOption = Objects.requireNonNull(cancelOption);
_defaultOption = Objects.requireNonNull(defaultOption);
_icon = icon;
_parent = parent;
}
/**
* This method allows you to specify the title of the dialog,
* which is the text that will be displayed in the title bar of the dialog window.
* If you don't specify a title, a default title may be used based on the dialog type,
* so a title does not need to be specified here for the dialog to be shown.
*
* @param title The title of the dialog.
* @return A new {@link ConfirmDialog} instance with the specified title.
*/
public ConfirmDialog titled( String title ) {
return new ConfirmDialog(_type, title, _message, _yesOption, _noOption, _cancelOption, _defaultOption, _icon, _parent);
}
/**
* This method allows you to specify some text that will be used to represent the {@link ConfirmAnswer#YES}
* option in the dialog. <br>
* So when the user clicks on that option, the dialog will return {@link ConfirmAnswer#YES}.
*
* @param yesOption The text of the "yes" option.
* @return A new {@link ConfirmDialog} instance with the specified "yes" option text.
*/
public ConfirmDialog yesOption( String yesOption ) {
return new ConfirmDialog(_type, _title, _message, yesOption, _noOption, _cancelOption, _defaultOption, _icon, _parent);
}
/**
* This method allows you to specify some text that will be used to represent the {@link ConfirmAnswer#NO}
* option in the dialog. <br>
* So when the user clicks on that option, the dialog will return {@link ConfirmAnswer#NO}.
*
* @param noOption The text of the "no" option.
* @return A new {@link ConfirmDialog} instance with the specified "no" option text.
*/
public ConfirmDialog noOption( String noOption ) {
return new ConfirmDialog(_type, _title, _message, _yesOption, noOption, _cancelOption, _defaultOption, _icon, _parent);
}
/**
* This method allows you to specify some text that will be used to represent the {@link ConfirmAnswer#CANCEL}
* option in the dialog. <br>
* So when the user clicks on that option, the dialog will return {@link ConfirmAnswer#CANCEL}.
*
* @param cancelOption The text of the "cancel" option.
* @return A new {@link ConfirmDialog} instance with the specified "cancel" option text.
*/
public ConfirmDialog cancelOption( String cancelOption ) {
return new ConfirmDialog(_type, _title, _message, _yesOption, _noOption, cancelOption, _defaultOption, _icon, _parent);
}
/**
* Use this to specify the default option for the dialog, which is the option
* which will have the initial focus when the dialog is shown. <br>
* So when the user presses the "Enter" key, the dialog will return the option
* that was set as the default option.
*
* @param defaultOption The text of the default option.
* @return A new {@link ConfirmDialog} instance with the specified default option text.
*/
public ConfirmDialog defaultOption( ConfirmAnswer defaultOption ) {
return new ConfirmDialog(_type, _title, _message, _yesOption, _noOption, _cancelOption, defaultOption, _icon, _parent);
}
/**
* Use this to specify the icon for the confirm dialog through an {@link IconDeclaration},
* which is a constant that represents the icon resource with a preferred size.
* The icon will be loaded and cached automatically for you when using the declaration based approach.
*
* @param icon The icon declaration of the dialog, which may contain the path to the icon resource.
* @return A new {@link ConfirmDialog} instance with the specified icon declaration.
*/
public ConfirmDialog icon( IconDeclaration icon ) {
Objects.requireNonNull(icon);
return icon.find().map(this::icon).orElse(this);
}
/**
* Defines the icon for the dialog, whose appearance and position may vary depending on the
* look and feel of the current system.
* Consider using the {@link #icon(IconDeclaration)} method over this one
* as it reduces the risk of icon loading issues.
*
* @param icon The icon of the dialog.
* @return A new {@link ConfirmDialog} instance with the specified icon.
*/
public ConfirmDialog icon( Icon icon ) {
return new ConfirmDialog(_type, _title, _message, _yesOption, _noOption, _cancelOption, _defaultOption, icon, _parent);
}
/**
* Use this to display a custom icon in the dialog by
* providing the path to the icon resource.
* Consider using the {@link #icon(IconDeclaration)} method over this one
* as you may also specify the preferred size of the icon through the declaration.
*
* @param path The path to the icon of the dialog.
* @return A new {@link ConfirmDialog} instance with the specified icon.
*/
public ConfirmDialog icon( String path ) {
Objects.requireNonNull(path);
return new ConfirmDialog(_type, _title, _message, _yesOption, _noOption, _cancelOption, _defaultOption, UI.findIcon(path).orElse(null), _parent);
}
/**
* Allows you to specify the parent component of the dialog, which is the component
* that the dialog will be centered on top of. <br>
* This is useful when you want to make the dialog modal to a specific component,
* but a parent component is not required to show the dialog.
*
* @param parent The parent component of the dialog.
* @return A new {@link ConfirmDialog} instance with the specified parent component.
*/
public ConfirmDialog parent( Component parent ) {
return new ConfirmDialog(_type, _title, _message, _yesOption, _noOption, _cancelOption, _defaultOption, _icon, parent);
}
/**
* @param type The type of the dialog, which may be one of the following:
* <ul>
* <li>{@link JOptionPane#ERROR_MESSAGE}</li>
* <li>{@link JOptionPane#INFORMATION_MESSAGE}</li>
* <li>{@link JOptionPane#WARNING_MESSAGE}</li>
* <li>{@link JOptionPane#PLAIN_MESSAGE}</li>
* <li>{@link JOptionPane#QUESTION_MESSAGE}</li>
* </ul>
* @return A new {@link ConfirmDialog} instance with the specified type.
*/
private ConfirmDialog _type( int type ) {
return new ConfirmDialog(type, _title, _message, _yesOption, _noOption, _cancelOption, _defaultOption, _icon, _parent);
}
/**
* Shows the confirmation dialog as a question dialog (see {@link JOptionPane#QUESTION_MESSAGE}) and returns the
* {@link ConfirmAnswer} that the user selected in the dialog. <br>
* Note that this method is blocking and will only return when the user has selected
* an option in the dialog.
*
* @return The {@link ConfirmAnswer} that the user selected in the dialog.
*/
public ConfirmAnswer showAsQuestion() {
return _type(JOptionPane.QUESTION_MESSAGE).show();
}
/**
* Shows the confirmation dialog as an error dialog (see {@link JOptionPane#ERROR_MESSAGE}) and returns the
* {@link ConfirmAnswer} that the user selected in the dialog. <br>
* Note that this method is blocking and will only return when the user has selected
* an option in the dialog.
*
* @return The {@link ConfirmAnswer} that the user selected in the dialog.
*/
public ConfirmAnswer showAsError() {
return _type(JOptionPane.ERROR_MESSAGE).show();
}
/**
* Shows the confirmation dialog as an info dialog (see {@link JOptionPane#INFORMATION_MESSAGE}) and returns the
* {@link ConfirmAnswer} that the user selected in the dialog. <br>
* Note that this method is blocking and will only return when the user has selected
* an option in the dialog.
*
* @return The {@link ConfirmAnswer} that the user selected in the dialog.
*/
public ConfirmAnswer showAsInfo() {
return _type(JOptionPane.INFORMATION_MESSAGE).show();
}
/**
* Shows the confirmation dialog as a warning dialog (see {@link JOptionPane#WARNING_MESSAGE}) and returns the
* {@link ConfirmAnswer} that the user selected in the dialog. <br>
* Note that this method is blocking and will only return when the user has selected
* an option in the dialog.
*
* @return The {@link ConfirmAnswer} that the user selected in the dialog.
*/
public ConfirmAnswer showAsWarning() {
return _type(JOptionPane.WARNING_MESSAGE).show();
}
/**
* Shows the confirmation dialog as a plain dialog (see {@link JOptionPane#PLAIN_MESSAGE}) and returns the
* {@link ConfirmAnswer} that the user selected in the dialog. <br>
* Note that this method is blocking and will only return when the user has selected
* an option in the dialog.
*
* @return The {@link ConfirmAnswer} that the user selected in the dialog.
*/
public ConfirmAnswer showPlain() {
return _type(JOptionPane.PLAIN_MESSAGE).show();
}
/**
* Use this to summon the dialog with the current settings and wait
* for the user to select an option. <br>
* The answer will be returned as a {@link ConfirmAnswer} enum.
* Note that this method is blocking and will only return when the user has selected
* an option in the dialog.
*
* @return The {@link ConfirmAnswer} that the user selected in the dialog.
*/
public ConfirmAnswer show() {
try {
return UI.runAndGet(() -> {
String yes = _yesOption.trim();
String no = _noOption.trim();
String cancel = _cancelOption.trim();
List<Object> options = new ArrayList<>();
if ( !yes.isEmpty() )
options.add(yes);
if ( !yes.isEmpty() && !no.isEmpty() )
options.add(no);
if ( !cancel.isEmpty() && !options.isEmpty() )
options.add(cancel);
int optionsType = JOptionPane.DEFAULT_OPTION;
if ( !yes.isEmpty() && no.isEmpty() && cancel.isEmpty() )
optionsType = JOptionPane.OK_OPTION;
if ( !yes.isEmpty() && !no.isEmpty() && cancel.isEmpty() )
optionsType = JOptionPane.YES_NO_OPTION;
if ( !yes.isEmpty() && !no.isEmpty() && !cancel.isEmpty() )
optionsType = JOptionPane.YES_NO_CANCEL_OPTION;
if ( !yes.isEmpty() && no.isEmpty() && !cancel.isEmpty() )
optionsType = JOptionPane.OK_CANCEL_OPTION;
int type = _type;
if ( type == -1 ) {
if ( optionsType == JOptionPane.YES_NO_OPTION || optionsType == JOptionPane.YES_NO_CANCEL_OPTION )
type = JOptionPane.QUESTION_MESSAGE;
else
type = JOptionPane.PLAIN_MESSAGE;
}
String title = _title.trim();
if ( title.isEmpty() ) {
if ( type == JOptionPane.QUESTION_MESSAGE )
title = "Confirm";
if ( type == JOptionPane.ERROR_MESSAGE )
title = "Error";
if ( type == JOptionPane.INFORMATION_MESSAGE )
title = "Info";
if ( type == JOptionPane.WARNING_MESSAGE )
title = "Warning";
if ( type == JOptionPane.PLAIN_MESSAGE )
title = "Message";
}
String defaultOption = "";
switch ( _defaultOption ) {
case YES: defaultOption = yes; break;
case NO: defaultOption = no; break;
case CANCEL: defaultOption = cancel; break;
default: break;
}
return ConfirmAnswer.from(Context.summoner.showOptionDialog(
_parent, _message, title, optionsType,
type, _icon, options.toArray(), defaultOption
));
});
} catch (Exception e) {
log.error("Failed to show confirm dialog, returning 'CANCEL' as dialog result!", e);
return ConfirmAnswer.CANCEL;
}
}
}