All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.controlsfx.dialog.Dialog Maven / Gradle / Ivy

Go to download

High quality UI controls and other tools to complement the core JavaFX distribution

There is a newer version: 11.2.1
Show newest version
/**
 * Copyright (c) 2013, 2014 ControlsFX
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *     * Neither the name of ControlsFX, any associated website, nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.controlsfx.dialog;

import impl.org.controlsfx.i18n.Localization;

import java.util.ArrayList;
import java.util.List;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.effect.Effect;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.text.TextAlignment;
import javafx.stage.Window;

import org.controlsfx.control.ButtonBar;
import org.controlsfx.control.ButtonBar.ButtonType;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.controlsfx.tools.Utils;

/**
 * A lower-level API for creating standardized dialogs consisting of the following
 * subsections:
 * 
 * 
    *
  • Title, *
  • System buttons (min, max, close), *
  • Masthead, *
  • Content, *
  • Expandable content, *
  • Button bar *
* *

This is more easily demonstrated in the diagram shown below: * *
*

* *

For developers wanting a simpler API, consider using the high-level * {@link Dialogs} class, which provides a simpler, fluent API for displaying * the most common types of dialog. * *

Code Examples

* *

Getting The User Response
The most important point to * note about the dialogs is that they are modal, * which means that they stop the application code from progressing until the * dialog is closed. Because of this, it is very easy to retrieve the users input: * when the user closes the dialog (e.g. by clicking on one of the buttons), the * dialog will be hidden, and their response will be returned from the * {@link #show() show} method that was called to bring the dialog up in the * first place. In other words, you might do the following: * *

 * {@code 
 * Action response = Dialogs.create()
 *      .title("You do want dialogs right?")
 *      .masthead("Just Checkin'")
 *      .message( "I was a bit worried that you might not want them, so I wanted to double check.")
 *      .showConfirm();
 *      
 * if (response == Dialog.Actions.OK) {
 *     // ... submit user input
 * } else {
 *     // ... user cancelled, reset form to default
 * }}
* *

Custom Dialogs
It is not always the case that one of * the pre-built {@link Dialogs} suits the requirements of your application. In * that case, you can build a custom dialog. Shown below is a screenshot of one * such custom dialog, and below that the source code used to create the code. * I hope you'll agree that the code is logical and simple! * *
*

* *
* *
 * {@code
 * // This dialog will consist of two input fields (username and password),
 * // and have two buttons: Login and Cancel.
 * 
 * final TextField txUserName = new TextField();
 * final PasswordField txPassword = new PasswordField();
 * final Action actionLogin = new AbstractAction("Login") {
 *     {  
 *         ButtonBar.setType(this, ButtonType.OK_DONE); 
 *     }
 *       
 *     // This method is called when the login button is clicked...
 *     public void execute(ActionEvent ae) {
 *          Dialog dlg = (Dialog) ae.getSource();
 *          // real login code here
 *          dlg.hide();
 *      }
 * };
 *   
 * // This method is called when the user types into the username / password fields  
 * private void validate() {
 *     actionLogin.disabledProperty().set( 
 *           txUserName.getText().trim().isEmpty() || txPassword.getText().trim().isEmpty());
 * }
 *   
 * // Imagine that this method is called somewhere in your codebase
 * private void showLoginDialog() {
 *     Dialog dlg = new Dialog(null, "Login Dialog");
 *       
 *     // listen to user input on dialog (to enable / disable the login button)
 *     ChangeListener changeListener = new ChangeListener() {
 *         public void changed(ObservableValue observable, String oldValue, String newValue) {
 *             validate();
 *         }
 *     };
 *     txUserName.textProperty().addListener(changeListener);
 *     txPassword.textProperty().addListener(changeListener);
 *       
 *     // layout a custom GridPane containing the input fields and labels
 *     final GridPane content = new GridPane();
 *     content.setHgap(10);
 *     content.setVgap(10);
 *       
 *     content.add(new Label("User name"), 0, 0);
 *     content.add(txUserName, 1, 0);
 *     GridPane.setHgrow(txUserName, Priority.ALWAYS);
 *     content.add(new Label("Password"), 0, 1);
 *     content.add(txPassword, 1, 1);
 *     GridPane.setHgrow(txPassword, Priority.ALWAYS);
 *       
 *     // create the dialog with a custom graphic and the gridpane above as the
 *     // main content region
 *     dlg.setResizable(false);
 *     dlg.setIconifiable(false);
 *     dlg.setGraphic(new ImageView(HelloDialog.class.getResource("login.png").toString()));
 *     dlg.setContent(content);
 *     dlg.getActions().addAll(actionLogin, Dialog.Actions.CANCEL);
 *     validate();
 *       
 *     // request focus on the username field by default (so the user can
 *     // type immediately without having to click first)
 *     Platform.runLater(new Runnable() {
 *         public void run() {
 *             txUserName.requestFocus();
 *         }
 *     });
 *
 *     dlg.show();
 * }
 * }
* * @see Dialogs * @see Action */ @Deprecated public class Dialog { /************************************************************************** * * Static fields * **************************************************************************/ /** * Defines a native dialog style. * The dialogs rendered using this style will have a native title bar. */ public static final String STYLE_CLASS_NATIVE = "native"; //$NON-NLS-1$ /** * Defines a cross-platform dialog style. * The dialogs rendered using this style will have a cross-platform title bar. */ public static final String STYLE_CLASS_CROSS_PLATFORM = "cross-platform"; //$NON-NLS-1$ /** * Defines a dialog style with no decorations. * The dialogs rendered using this style will not have a title bar. */ public static final String STYLE_CLASS_UNDECORATED = "undecorated"; //$NON-NLS-1$ /** * An action that, by default, will show 'Cancel'. */ public static final Action ACTION_CANCEL = new DialogAction( Localization.asKey("dlg.cancel.button"), ButtonType.CANCEL_CLOSE ){ //$NON-NLS-1$ { lock();} @Override public String toString() { return "DialogAction.CANCEL";} //$NON-NLS-1$ }; /** * An action that, by default, will show 'Close'. */ public static final Action ACTION_CLOSE = new DialogAction( Localization.asKey("dlg.close.button"), ButtonType.CANCEL_CLOSE ){ //$NON-NLS-1$ { lock();} @Override public String toString() { return "DialogAction.CLOSE";} //$NON-NLS-1$ }; /** * An action that, by default, will show 'No'. */ public static final Action ACTION_NO = new DialogAction( Localization.asKey("dlg.no.button"), ButtonType.NO ){ //$NON-NLS-1$ { lock();} @Override public String toString() { return "DialogAction.NO";} //$NON-NLS-1$ }; /** * An action that, by default, will show 'OK'. */ public static final Action ACTION_OK = new DialogAction( Localization.asKey("dlg.ok.button"), ButtonType.OK_DONE, false, true, true){ //$NON-NLS-1$ { lock();} @Override public String toString() { return "DialogAction.OK";} //$NON-NLS-1$ }; /** * An action that, by default, will show 'Yes'. */ public static final Action ACTION_YES = new DialogAction( Localization.asKey("dlg.yes.button"), ButtonType.YES, false, true, true ){ //$NON-NLS-1$ { lock();} @Override public String toString() { return "DialogAction.YES";} //$NON-NLS-1$ }; // enable to turn on grid lines, etc private static final boolean DEBUG = false; static int MIN_DIALOG_WIDTH = 426; /************************************************************************** * * Private fields * **************************************************************************/ private final FXDialog dialog; // Dialog result, package-protected for Dialogs Action result = ACTION_CANCEL; private GridPane contentPane; // used to represent whether the masthead being used is generated by the // developer passing in a String, or whether they passed in a Node. If it is // a String, this is true. Otherwise it is false. private boolean isDefaultMasthead = false; /************************************************************************** * * Constructors * **************************************************************************/ /** * Creates a heavyweight dialog using specified owner and title. * * @param owner The dialog window owner - if specified the dialog will be * centered over the owner, otherwise the dialog will be shown in the * middle of the screen. * @param title The dialog title to be shown at the top of the dialog. */ public Dialog(Object owner, String title) { this(owner, title, false); } /** * Creates a dialog using specified owner and title, which may be rendered * in either a heavyweight or lightweight fashion. * * @param owner The dialog window owner - if specified the dialog will be * centered over the owner, otherwise the dialog will be shown in the * middle of the screen. * @param title The dialog title to be shown at the top of the dialog. * @param lightweight If true this dialog will be rendered inside the given * owner, rather than in a separate window (as heavyweight dialogs are). * Refer to the {@link Dialogs} class documentation for more details on * the difference between heavyweight and lightweight dialogs. */ public Dialog(Object owner, String title, boolean lightweight) { if (lightweight) { this.dialog = new LightweightDialog(title, owner); } else { Window window = Utils.getWindow(owner); this.dialog = new HeavyweightDialog(title, window); this.dialog.setModal(true); } updateStyleClasses(dialog.getStyleClass(), false); // if the actions change, we dynamically update the dialog getActions().addListener((ListChangeListener) c -> { if (dialog.getWindow().isShowing()) { updateDialogContent(); } }); } void updateStyleClasses(List styleClasses, boolean addUnique) { // let's check to see what styleclasses have been set. If any have been // set outside the 'common' set, we will not set the default cross-platform // style class, otherwise we will if (styleClasses == null) return; for (String styleClass : styleClasses) { if (addUnique && ! dialog.getStyleClass().contains(styleClass)) { dialog.getStyleClass().add(styleClass); } } } /************************************************************************** * * Public API * **************************************************************************/ /** * Shows the dialog and waits for the user response (in other words, brings * up a modal dialog, with the returned value the users input). * * @return The {@link Action} used to close the dialog. */ public Action show() { try { updateDialogContent(); dialog.show(); return result; } catch (Throwable e) { e.printStackTrace(); return ACTION_CANCEL; } } /** * Hides the dialog. */ public void hide() { dialog.hide(); } /** * Visibly shakes the dialog to get the users attention. */ public void shake() { dialog.shake(); } /** * Assigns the resulting action. If action is a {@link DialogAction} and has either CANCEL or CLOSING traits * the dialog will be closed. * @param result */ public void setResult(Action result) { this.result = result; if (result instanceof DialogAction) { DialogAction dlgAction = (DialogAction) result; if (dlgAction.isCancel() || dlgAction.isClosing()) { hide(); } } } /** * Return the StyleSheets associated with the scene used in the Dialog (see {@link Scene#getStylesheets()} * This allow you to specify custom CSS rules to be applied on your dialog's elements. */ public ObservableList getStylesheets(){ return dialog.getStylesheets(); } /** * Return the style classes specified on this dialog instance. */ public ObservableList getStyleClass() { return dialog.getStyleClass(); } /************************************************************************** * * Properties * **************************************************************************/ // --- resizable /** * Represents whether the dialog is resizable. */ public BooleanProperty resizableProperty() { return dialog.resizableProperty(); } /** * Returns whether or not the dialog is resizable. * * @return true if dialog is resizable. */ public final boolean isResizable() { return resizableProperty().get(); } /** * Sets whether the dialog can be resized by the user. * Resizable dialogs can also be maximized ( maximize button * becomes visible) * * @param resizable true if dialog should be resizable. */ public final void setResizable(boolean resizable) { resizableProperty().set(resizable); } /** * Sets whether the dialog can be iconified (minimized) * @param iconifiable if dialog should be iconifiable */ public void setIconifiable(boolean iconifiable) { dialog.setIconifiable(iconifiable); } /** * Sets whether the dialog can be closed * @param closable if dialog should be closable */ public void setClosable( boolean closable ) { dialog.setClosable( closable ); } // --- graphic private final ObjectProperty graphicProperty = new SimpleObjectProperty() { @Override protected void invalidated() { updateGraphic(); } }; /** * The dialog graphic, presented either in the masthead, if one is showing, * or to the left of the {@link #contentProperty() content}. * * @return An ObjectProperty wrapping the current graphic. */ public ObjectProperty graphicProperty() { return graphicProperty; } /** * @see #graphicProperty() * @return The currently set dialog graphic. */ public final Node getGraphic() { return graphicProperty.get(); } /** * Sets the dialog graphic, which will be displayed either in the masthead, * if one is showing, or to the left of the {@link #contentProperty() content}. * * @param graphic The new dialog graphic, or null if no graphic should be shown. */ public final void setGraphic(Node graphic) { this.graphicProperty.set(graphic); } // --- masthead private final ObjectProperty masthead = new SimpleObjectProperty() { @Override protected void invalidated() { // we don't know where this masthead come from, so we reset the // default masthead flag to false. isDefaultMasthead = false; } }; /** * Node which acts as dialog's masthead * @return dialog's masthead */ public final Node getMasthead() { return masthead.get(); } /** * Assigns dialog's masthead. Any Node can be used * @param masthead future masthead */ public final void setMasthead(Node masthead) { this.masthead.setValue(masthead); } /** * Sets the string to show in the dialog masthead area. * * @param mastheadText The masthead text to show in the masthead area. */ public final void setMasthead(String mastheadText) { if (mastheadText == null) { return; } BorderPane mastheadPanel = new BorderPane(); mastheadPanel.getStyleClass().add("masthead-panel"); //$NON-NLS-1$ // on left of masthead is the text Label mastheadLabel = new Label(mastheadText); mastheadLabel.setWrapText(true); mastheadLabel.setAlignment(Pos.CENTER_LEFT); mastheadLabel.setMaxWidth(MIN_DIALOG_WIDTH); mastheadPanel.setLeft(mastheadLabel); BorderPane.setAlignment(mastheadLabel, Pos.CENTER_LEFT); setMasthead(mastheadPanel); // after we set the masthead, we reset the isDefaultMasthead flag to // true, as we know this is a default masthead isDefaultMasthead = true; // on the right of the masthead is a graphic, if one is specified updateGraphic(); } private void updateGraphic() { final Node graphic = getGraphic(); final Node masthead = getMasthead(); if (masthead != null) { // we need to update the graphic on the right of the masthead area, // but we only do this if we have a default masthead. if (isDefaultMasthead) { BorderPane mastheadBorderPane = (BorderPane)masthead; if (graphic == null) { mastheadBorderPane.setRight(null); } else { StackPane pane = new StackPane(graphic); pane.getStyleClass().add("graphic-container"); //$NON-NLS-1$ mastheadBorderPane.setRight(pane); } } } } /** * Property representing the masthead area of the dialog. */ public ObjectProperty mastheadProperty() { return masthead; } // --- content private final ObjectProperty content = new SimpleObjectProperty() { @Override protected void invalidated() { Node contentNode = getContent(); if (contentNode != null) { contentNode.getStyleClass().addAll("content"); //$NON-NLS-1$ } if (dialog.getWindow().isShowing()) { updateDialogContent(); } }; }; /** * Returns the dialog content as a Node (even if it was set as a String using * {@link #setContent(String)} - this was simply transformed into a * {@link Node} (most probably a {@link Label}). * * @return dialog's content */ public final Node getContent() { return content.get(); } /** * Assign dialog content. Any Node can be used * @param content dialog's content */ public final void setContent(Node content) { this.content.setValue(content); } /** * Assign text as the dialog's content (this will be transformed into a * {@link Node} and then set via {@link #setContent(Node)}). * * @param contentText The text to show in the main section of the dialog. */ public final void setContent(String contentText) { if (contentText == null) return; Label label = new Label(contentText); label.setAlignment(Pos.TOP_LEFT); label.setTextAlignment(TextAlignment.LEFT); label.setMaxWidth(Double.MAX_VALUE); label.setMaxHeight(Double.MAX_VALUE); // FIXME we don't want to restrict the width, but for now this works ok // label.setPrefWidth(MAIN_TEXT_WIDTH); label.setMaxWidth(360); label.setWrapText(true); setContent(label); } /** * Property representing the content area of the dialog. */ public ObjectProperty contentProperty() { return content; } // --- expandable content private final ObjectProperty expandableContentProperty = new SimpleObjectProperty<>(); /** * A property that represents the dialog expandable content area. Any Node * can be placed in this area, but it will only be shown when the user clicks * the 'Show Details' expandable button. This button will be added automatically * when the expandable content property is non-null. */ public ObjectProperty expandableContentProperty() { return expandableContentProperty; } /** * Returns the dialog expandable content node, if one is set, or null otherwise. */ public final Node getExpandableContent() { return expandableContentProperty.get(); } /** * Sets the dialog expandable content node, or null if no expandable content * needs to be shown. */ public final void setExpandableContent(Node content) { this.expandableContentProperty.set(content); } // --- width /** * Property representing the width of the dialog. */ ReadOnlyDoubleProperty widthProperty() { return dialog.widthProperty(); } /** * Returns the width of the dialog. */ public final double getWidth() { return widthProperty().get(); } // --- height /** * Property representing the height of the dialog. */ ReadOnlyDoubleProperty heightProperty() { return dialog.heightProperty(); } /** * Returns the height of the dialog. */ public final double getHeight() { return heightProperty().get(); } // --- actions private final ObservableList actions = FXCollections. observableArrayList(); /** * Observable list of actions used for the dialog {@link ButtonBar}. * Modifying the contents of this list will change the actions available to * the user. * @return The {@link ObservableList} of actions available to the user. */ public final ObservableList getActions() { return actions; } /** * Return the titleProperty of the dialog. */ public StringProperty titleProperty(){ return this.dialog.titleProperty(); } /** * Return the title of the dialog. */ public String getTitle(){ return this.dialog.titleProperty().get(); } /** * Change the Title of the dialog. * @param title */ public void setTitle(String title){ this.dialog.titleProperty().set(title); } // --- background effect private final ObjectProperty backgroundEffectProperty = new SimpleObjectProperty() { @Override protected void invalidated() { dialog.setEffect(getValue()); } }; /** * Background effect property of the dialog */ public ObjectProperty backgroundEffectProperty() { return backgroundEffectProperty; } /** * Gets background effect of the dialog */ public final Effect getBackgroundEffect() { return backgroundEffectProperty.get(); } /** * Sets background effect of the dialog */ public final void setBackgroundEffect(Effect effect) { this.backgroundEffectProperty.set(effect); } /************************************************************************** * * Support classes * **************************************************************************/ /************************************************************************** * * Private Implementation * **************************************************************************/ // TODO we use this with the login dialog to change the content, but the longer // term solution is to automatically call sizeToScene on the FXDialog when // the content changes. void sizeToScene() { dialog.sizeToScene(); } /** * TODO delete me - this is just for testing!! */ static String buttonBarOrder = null; static void setMacOS(boolean b) { if (b) { buttonBarOrder = ButtonBar.BUTTON_ORDER_MAC_OS; } } static void setWindows(boolean b) { if (b) { buttonBarOrder = ButtonBar.BUTTON_ORDER_WINDOWS; } } static void setLinux(boolean b) { if (b) { buttonBarOrder = ButtonBar.BUTTON_ORDER_LINUX; } } // -- end of testing code private boolean hasMasthead() { return getMasthead() != null; } private boolean hasExpandableContent() { return getExpandableContent() != null; } private void updateDialogContent() { this.contentPane = new GridPane(); this.contentPane.getStyleClass().add("content-pane"); //$NON-NLS-1$ this.contentPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); int row = 0; final boolean hasMasthead = hasMasthead(); if (hasMasthead) { Node masthead = getMasthead(); contentPane.add(masthead, 0, row++); } createCenterPanel(row); Node root = dialog.getRoot(); root.pseudoClassStateChanged(MASTHEAD_PSEUDO_CLASS, hasMasthead); root.pseudoClassStateChanged(NO_MASTHEAD_PSEUDO_CLASS, !hasMasthead); // the dialog has a simple grid layout. If there is a masthead, it consists // of a single column. If there is no masthead, there are two columns. // We always want the right-most column to expand to fill all available // width. ColumnConstraints leftColumn = new ColumnConstraints(); ColumnConstraints rightColumn = new ColumnConstraints(); rightColumn.setFillWidth(true); rightColumn.setHgrow(Priority.ALWAYS); if (! hasMasthead) { contentPane.getColumnConstraints().add(leftColumn); } contentPane.getColumnConstraints().add(rightColumn); // now that all the content is built, we apply the min size rules if (! contentPane.minWidthProperty().isBound()) { contentPane.setMinWidth(MIN_DIALOG_WIDTH); } this.contentPane.setGridLinesVisible(DEBUG); this.dialog.setContentPane(contentPane); } private void createCenterPanel(final int startRow) { final boolean hasMasthead = hasMasthead(); final Node content = getContent(); if (content != null) { content.getStyleClass().add("center"); //$NON-NLS-1$ contentPane.add(content, hasMasthead ? 0 : 1, startRow); GridPane.setVgrow(content, Priority.SOMETIMES); GridPane.setValignment(content, VPos.TOP); GridPane.setHgrow(content, Priority.ALWAYS); if (content instanceof Region) { contentPane.minWidthProperty().bind(new DoubleBinding() { { bind(((Region)content).minWidthProperty()); } @Override protected double computeValue() { return Math.max(MIN_DIALOG_WIDTH, ((Region) content).getMinWidth()); } }); } } // dialog image can go to the left if there is no masthead final Node graphic = getGraphic(); if (!hasMasthead && graphic != null) { Pane graphicPane = new Pane(graphic); final double w = graphic.getLayoutBounds().getWidth(); final double h = graphic.getLayoutBounds().getHeight(); graphicPane.setMinSize(w, h); graphicPane.getStyleClass().add("graphic"); //$NON-NLS-1$ contentPane.add(graphicPane, 0, startRow); GridPane.setValignment(graphicPane, VPos.TOP); GridPane.setMargin(graphicPane, new Insets(0,8,0,0)); } if (hasExpandableContent()) { Node ec = getExpandableContent(); ec.getStyleClass().add("expandable-content"); //$NON-NLS-1$ contentPane.add(ec, 0, startRow + 1, 2, 1); ec.setVisible(false); ec.managedProperty().bind(ec.visibleProperty()); GridPane.setHgrow(ec, Priority.ALWAYS); GridPane.setVgrow(ec, Priority.ALWAYS); } if ( !getActions().isEmpty() || hasExpandableContent()) { createButtonPanel(startRow + 2); } } private void createButtonPanel(final int startRow) { ButtonBar buttonBar = new ButtonBar(); if (buttonBarOrder != null) { buttonBar.setButtonOrder(buttonBarOrder); } // show details button if expandable content is present if (hasExpandableContent()) { buttonBar.addSizeIndependentButton(createDetailsButton(), ButtonType.HELP_2); } List buttons = new ArrayList<>(); boolean hasDefault = false; for (Action cmd : getActions()) { ButtonBase b = createButton(cmd, !hasDefault); // keep only first default button if (b instanceof Button) { hasDefault |= ((Button) b).isDefaultButton(); } buttons.add(b); } buttonBar.getButtons().addAll(buttons); contentPane.add(buttonBar, 0, startRow, 2, 1); GridPane.setHgrow(buttonBar, Priority.ALWAYS); GridPane.setVgrow(buttonBar, Priority.NEVER); GridPane.setMargin(buttonBar, new Insets(14, 0, 14, 0)); // GridPane.setValignment(buttonBar, VPos.BASELINE); } private Hyperlink createDetailsButton() { final Hyperlink detailsButton = new Hyperlink(); detailsButton.getStyleClass().setAll("details-button", "more"); //$NON-NLS-1$ //$NON-NLS-2$ final String moreText = Localization.getString("dlg.detail.button.more"); //$NON-NLS-1$ final String lessText = Localization.getString("dlg.detail.button.less"); //$NON-NLS-1$ detailsButton.setText(moreText); detailsButton.setOnAction(new EventHandler() { @Override public void handle(ActionEvent ae) { final Node content = getExpandableContent(); setResizable(!content.isVisible()); content.setVisible(!content.isVisible()); detailsButton.setText(content.isVisible() ? lessText : moreText); detailsButton.getStyleClass().setAll("details-button", (content.isVisible() ? "less" : "more")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ dialog.sizeToScene(); } }); return detailsButton; } private Button createButton(final Action action, boolean keepDefault) { final Button button = ActionUtils.createButton(action); if (action instanceof DialogAction) { DialogAction dlgAction = (DialogAction) action; button.setDefaultButton(keepDefault && dlgAction.isDefault()); button.setCancelButton(dlgAction.isCancel()); } button.setOnAction(new EventHandler() { @Override public void handle(ActionEvent ae) { action.handle(new ActionEvent(Dialog.this, ae.getTarget())); result = action; } }); return button; } /*************************************************************************** * * Stylesheet Handling * **************************************************************************/ private static final PseudoClass MASTHEAD_PSEUDO_CLASS = PseudoClass.getPseudoClass("masthead"); //$NON-NLS-1$ private static final PseudoClass NO_MASTHEAD_PSEUDO_CLASS = PseudoClass.getPseudoClass("no-masthead"); //$NON-NLS-1$ }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy