
org.controlsfx.dialog.Wizard Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of controlsfx Show documentation
Show all versions of controlsfx Show documentation
High quality UI controls and other tools to complement the core JavaFX distribution
/**
* Copyright (c) 2014, 2015 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 static impl.org.controlsfx.i18n.Localization.asKey;
import static impl.org.controlsfx.i18n.Localization.localize;
import impl.org.controlsfx.ImplUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Stack;
import java.util.function.BooleanSupplier;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
import javafx.scene.layout.Pane;
import javafx.stage.Screen;
import javafx.stage.Window;
import org.controlsfx.tools.ValueExtractor;
import org.controlsfx.validation.ValidationSupport;
/**
* The API for creating multi-page Wizards, based on JavaFX {@link Dialog} API.
* Wizard can be setup in following few steps:
*
* - Design wizard pages by inheriting them from {@link WizardPane}
* - Define wizard flow by implementing {@link Wizard.Flow}
* - Create and instance of the Wizard and assign flow to it
* - Execute the wizard using showAndWait method
* - Values can be extracted from settings map by calling getSettings
*
* For simple, linear wizards, the {@link LinearFlow} can be used.
* It is a flow based on a collection of wizard pages. Here is the example:
*
* {@code // Create pages. Here for simplicity we just create and instance of WizardPane.
* WizardPane page1 = new WizardPane();
* WizardPane page2 = new WizardPane();
* WizardPane page2 = new WizardPane();
*
* // create wizard
* Wizard wizard = new Wizard();
*
* // create and assign the flow
* wizard.setFlow(new LinearFlow(page1, page2, page3));
*
* // show wizard and wait for response
* wizard.showAndWait().ifPresent(result -> {
* if (result == ButtonType.FINISH) {
* System.out.println("Wizard finished, settings: " + wizard.getSettings());
* }
* });}
*
* For more complex wizard flows we suggest to create a custom ones, describing page traversal logic.
* Here is a simplified example:
*
* {@code Wizard.Flow branchingFlow = new Wizard.Flow() {
* public Optional advance(WizardPane currentPage) {
* return Optional.of(getNext(currentPage));
* }
*
* public boolean canAdvance(WizardPane currentPage) {
* return currentPage != page3;
* }
*
* private WizardPane getNext(WizardPane currentPage) {
* if ( currentPage == null ) {
* return page1;
* } else if ( currentPage == page1) {
* // skipNextPage() does not exist - this just represents that you
* // can add a conditional statement here to change the page.
* return page1.skipNextPage()? page3: page2;
* } else {
* return page3;
* }
* }
* };}
*/
public class Wizard {
/**************************************************************************
*
* Private fields
*
**************************************************************************/
private Dialog dialog;
private final ObservableMap settings = FXCollections.observableHashMap();
private final Stack pageHistory = new Stack<>();
private Optional currentPage = Optional.empty();
private final BooleanProperty invalidProperty = new SimpleBooleanProperty(false);
// Read settings activated by default for backward compatibility
private final BooleanProperty readSettingsProperty = new SimpleBooleanProperty(true);
private final ButtonType BUTTON_PREVIOUS = new ButtonType(localize(asKey("wizard.previous.button")), ButtonData.BACK_PREVIOUS); //$NON-NLS-1$
private final EventHandler BUTTON_PREVIOUS_ACTION_HANDLER = actionEvent -> {
actionEvent.consume();
currentPage = Optional.ofNullable( pageHistory.isEmpty()? null: pageHistory.pop() );
updatePage(dialog,false);
};
private final ButtonType BUTTON_NEXT = new ButtonType(localize(asKey("wizard.next.button")), ButtonData.NEXT_FORWARD); //$NON-NLS-1$
private final EventHandler BUTTON_NEXT_ACTION_HANDLER = actionEvent -> {
actionEvent.consume();
currentPage.ifPresent(page->pageHistory.push(page));
currentPage = getFlow().advance(currentPage.orElse(null));
updatePage(dialog,true);
};
private final StringProperty titleProperty = new SimpleStringProperty();
/**************************************************************************
*
* Constructors
*
**************************************************************************/
/**
* Creates an instance of the wizard without an owner.
*/
public Wizard() {
this(null);
}
/**
* Creates an instance of the wizard with the given owner.
* @param owner The object from which the owner window is deduced (typically
* this is a Node, but it may also be a Scene or a Stage).
*/
public Wizard(Object owner) {
this(owner, ""); //$NON-NLS-1$
}
/**
* Creates an instance of the wizard with the given owner and title.
*
* @param owner The object from which the owner window is deduced (typically
* this is a Node, but it may also be a Scene or a Stage).
* @param title The wizard title.
*/
public Wizard(Object owner, String title) {
invalidProperty.addListener( (o, ov, nv) -> validateActionState());
dialog = new Dialog<>();
dialog.titleProperty().bind(this.titleProperty);
setTitle(title);
Window window = null;
if ( owner instanceof Window) {
window = (Window)owner;
} else if ( owner instanceof Node ) {
window = ((Node)owner).getScene().getWindow();
}
dialog.initOwner(window);
}
/**************************************************************************
*
* Public API
*
**************************************************************************/
// /**
// * Shows the wizard but does not wait for a user response (in other words,
// * this brings up a non-blocking dialog). Users of this API must either
// * poll the {@link #resultProperty() result property}, or else add a listener
// * to the result property to be informed of when it is set.
// */
// public final void show() {
// dialog.show();
// }
/**
* Shows the wizard and waits for the user response (in other words, brings
* up a blocking dialog, with the returned value the users input).
*
* @return An {@link Optional} that contains the result.
*/
public final Optional showAndWait() {
return dialog.showAndWait();
}
/**
* @return {@link Dialog#resultProperty()} of the {@link Dialog} representing this {@link Wizard}.
*/
public final ObjectProperty resultProperty() {
return dialog.resultProperty();
}
/**
* The settings map is the place where all data from pages is kept once the
* user moves on from the page, assuming there is a {@link ValueExtractor}
* that is capable of extracting a value out of the various fields on the page.
*/
public final ObservableMap getSettings() {
return settings;
}
/**************************************************************************
*
* Properties
*
**************************************************************************/
// --- title
/**
* Return the titleProperty of the wizard.
*/
public final StringProperty titleProperty() {
return titleProperty;
}
/**
* Return the title of the wizard.
*/
public final String getTitle() {
return titleProperty.get();
}
/**
* Change the Title of the wizard.
* @param title
*/
public final void setTitle( String title ) {
titleProperty.set(title);
}
// --- flow
/**
* The {@link Flow} property represents the flow of pages in the wizard.
*/
private ObjectProperty flow = new SimpleObjectProperty(new LinearFlow()) {
@Override protected void invalidated() {
updatePage(dialog,false);
}
@Override public void set(Flow flow) {
super.set(flow);
pageHistory.clear();
if ( flow != null ) {
currentPage = flow.advance(currentPage.orElse(null));
updatePage(dialog,true);
}
};
};
public final ObjectProperty flowProperty() {
return flow;
}
/**
* Returns the currently set {@link Flow}, which represents the flow of
* pages in the wizard.
*/
public final Flow getFlow() {
return flow.get();
}
/**
* Sets the {@link Flow}, which represents the flow of pages in the wizard.
*/
public final void setFlow(Flow flow) {
this.flow.set(flow);
}
// --- Properties
private static final Object USER_DATA_KEY = new Object();
// A map containing a set of properties for this Wizard
private ObservableMap
© 2015 - 2025 Weber Informatics LLC | Privacy Policy