package com.bekwam.examples.javafx.wizard;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.inject.Inject;
import com.google.inject.Injector;
import javafx.beans.binding.When;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Callback;
public class WizardController {
private final int INDICATOR_RADIUS = 10;
private final String CONTROLLER_KEY = "controller";
@FXML
VBox contentPanel;
@FXML
HBox hboxIndicators;
@FXML
Button btnNext, btnBack, btnCancel;
@Inject
Injector injector;
@Inject
WizardData model;
private final List<Parent> steps = new ArrayList<>();
private final IntegerProperty currentStep = new SimpleIntegerProperty(-1);
@FXML
public void initialize() throws Exception {
buildSteps();
initButtons();
buildIndicatorCircles();
setInitialContent();
}
private void initButtons() {
btnBack.disableProperty().bind( currentStep.lessThanOrEqualTo(0) );
btnNext.disableProperty().bind( currentStep.greaterThanOrEqualTo(steps.size()-1) );
btnCancel.textProperty().bind(
new When(
currentStep.lessThan(steps.size()-1)
)
.then("Cancel")
.otherwise("Start Over")
);
}
private void setInitialContent() {
currentStep.set( 0 ); // first element
contentPanel.getChildren().add( steps.get( currentStep.get() ));
}
private void buildIndicatorCircles() {
for( int i=0; i<steps.size(); i++ ) {
hboxIndicators.getChildren().add( createIndicatorCircle(i));
}
}
private void buildSteps() throws java.io.IOException {
final JavaFXBuilderFactory bf = new JavaFXBuilderFactory();
final Callback<Class<?>, Object> cb = (clazz) -> injector.getInstance(clazz);
FXMLLoader fxmlLoaderStep1 = new FXMLLoader( WizardController.class.getResource("/wizard-fxml/Step1.fxml"), null, bf, cb);
Parent step1 = fxmlLoaderStep1.load( );
step1.getProperties().put( CONTROLLER_KEY, fxmlLoaderStep1.getController() );
FXMLLoader fxmlLoaderStep2 = new FXMLLoader( WizardController.class.getResource("/wizard-fxml/Step2.fxml"), null, bf, cb );
Parent step2 = fxmlLoaderStep2.load();
step2.getProperties().put( CONTROLLER_KEY, fxmlLoaderStep2.getController() );
FXMLLoader fxmlLoaderStep3 = new FXMLLoader(WizardController.class.getResource("/wizard-fxml/Step3.fxml"), null, bf, cb );
Parent step3 = fxmlLoaderStep3.load( );
step3.getProperties().put( CONTROLLER_KEY, fxmlLoaderStep3.getController() );
FXMLLoader fxmlLoaderConfirm = new FXMLLoader(WizardController.class.getResource("/wizard-fxml/Confirm.fxml"), null, bf, cb);
Parent confirm = fxmlLoaderConfirm.load( );
confirm.getProperties().put( CONTROLLER_KEY, fxmlLoaderConfirm.getController() );
FXMLLoader fxmlLoaderCompleted = new FXMLLoader( WizardController.class.getResource("/wizard-fxml/Completed.fxml"), null, bf, cb);
Parent completed = fxmlLoaderCompleted.load();
completed.getProperties().put( CONTROLLER_KEY, fxmlLoaderCompleted.getController() );
steps.addAll( Arrays.asList(
step1, step2, step3, confirm, completed
));
}
private Circle createIndicatorCircle(int i) {
Circle circle = new Circle(INDICATOR_RADIUS, Color.WHITE);
circle.setStroke(Color.BLACK);
circle.fillProperty().bind(
new When(
currentStep.greaterThanOrEqualTo(i))
.then(Color.DODGERBLUE)
.otherwise(Color.WHITE));
return circle;
}
@FXML
public void next() {
Parent p = steps.get(currentStep.get());
Object controller = p.getProperties().get(CONTROLLER_KEY);
// validate
Method v = getMethod( Validate.class, controller );
if( v != null ) {
try {
Object retval = v.invoke(controller);
if( retval != null && ((Boolean)retval) == false ) {
return;
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
// submit
Method sub = getMethod( Submit.class, controller );
if( sub != null ) {
try {
sub.invoke(controller);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
if( currentStep.get() < (steps.size()-1) ) {
contentPanel.getChildren().remove( steps.get(currentStep.get()) );
currentStep.set( currentStep.get() + 1 );
contentPanel.getChildren().add( steps.get(currentStep.get()) );
}
}
@FXML
public void back() {
if( currentStep.get() > 0 ) {
contentPanel.getChildren().remove( steps.get(currentStep.get()) );
currentStep.set( currentStep.get() - 1 );
contentPanel.getChildren().add( steps.get(currentStep.get()) );
}
}
@FXML
public void cancel() {
contentPanel.getChildren().remove( steps.get(currentStep.get()) );
currentStep.set( 0 ); // first screen
contentPanel.getChildren().add( steps.get(currentStep.get()) );
model.reset();
}
private Method getMethod(Class<? extends Annotation> an, Object obj) {
if( an == null ) {
return null;
}
if( obj == null ) {
return null;
}
Method[] methods = obj.getClass().getMethods();
if( methods != null && methods.length > 0 ) {
for( Method m : methods ) {
if( m.isAnnotationPresent(an)) {
return m;
}
}
}
return null;
}
}