Skip to content

Instantly share code, notes, and snippets.

@james-d
Created January 18, 2017 17:47
Show Gist options
  • Save james-d/7afa36cf02fba8963c2b306d3aa239b9 to your computer and use it in GitHub Desktop.
Save james-d/7afa36cf02fba8963c2b306d3aa239b9 to your computer and use it in GitHub Desktop.
<!-- in arrival package -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="arrival.ArrivalController">
<center>
<VBox spacing="5" alignment="CENTER">
<Label text="Pick Arrival Date"/>
<DatePicker fx:id="arrivalPicker"/>
</VBox>
</center>
<bottom>
<HBox spacing="5" alignment="CENTER">
<Button fx:id="nextButton" text="Next" onAction="#goToDeparture" />
</HBox>
</bottom>
</BorderPane>
package arrival;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import model.BookingModel;
public class ArrivalController {
private final BookingModel model ;
@FXML
private DatePicker arrivalPicker ;
@FXML
private Button nextButton ;
public ArrivalController(BookingModel model) {
this.model = model ;
}
public void initialize() {
arrivalPicker.valueProperty().bindBidirectional(model.arrivalProperty());
arrivalPicker.disableProperty().bind(model.confirmedProperty());
nextButton.disableProperty().bind(model.arrivalProperty().isNull());
}
@FXML
private void goToDeparture() {
model.setScreen(BookingModel.Screen.DEPARTURE);
}
}
package application;
import java.io.IOException;
import java.io.UncheckedIOException;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import model.BookingModel;
import model.BookingModel.Screen;
public class BookingApplication extends Application {
@Override
public void start(Stage primaryStage) {
SplitPane split = new SplitPane();
split.getItems().addAll(createBookingFlow(), createBookingFlow());
split.setOrientation(Orientation.VERTICAL);
Scene scene = new Scene(split, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Parent createBookingFlow() {
BookingModel model = new BookingModel() ;
model.setScreen(Screen.ARRIVAL);
ControllerFactory controllerFactory = new ControllerFactory(model);
BorderPane flow = new BorderPane();
Node arrivalScreen = load("arrival/Arrival.fxml", controllerFactory);
Node departureScreen = load("departure/Departure.fxml", controllerFactory);
Node confirmationScreen = load("confirmation/Confirmation.fxml", controllerFactory);
flow.centerProperty().bind(Bindings.createObjectBinding(() -> {
switch (model.getScreen()) {
case ARRIVAL: return arrivalScreen ;
case DEPARTURE: return departureScreen ;
case CONFIRMATION: return confirmationScreen ;
default: return null ;
}
}, model.screenProperty()));
return flow ;
}
private Node load(String resource, ControllerFactory controllerFactory) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource(resource));
loader.setControllerFactory(controllerFactory);
return loader.load() ;
} catch (IOException exc) {
throw new UncheckedIOException(exc);
}
}
public static void main(String[] args) {
launch(args);
}
}
package model;
import java.time.LocalDate;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
public class BookingModel {
private final ObjectProperty<LocalDate> arrival = new SimpleObjectProperty<>();
private final ObjectProperty<LocalDate> departure = new SimpleObjectProperty<>();
private final BooleanProperty confirmed = new SimpleBooleanProperty();
private final ObjectProperty<Screen> screen = new SimpleObjectProperty<>();
public enum Screen {
ARRIVAL, DEPARTURE, CONFIRMATION
}
public BookingModel() {
arrival.addListener((obs, oldArrival, newArrival) -> {
if (departure.get() == null || departure.get().equals(arrival.get()) || departure.get().isBefore(arrival.get())) {
departure.set(arrival.get().plusDays(1));
}
});
}
public final ObjectProperty<LocalDate> arrivalProperty() {
return this.arrival;
}
public final LocalDate getArrival() {
return this.arrivalProperty().get();
}
public final void setArrival(final LocalDate arrival) {
this.arrivalProperty().set(arrival);
}
public final ObjectProperty<LocalDate> departureProperty() {
return this.departure;
}
public final LocalDate getDeparture() {
return this.departureProperty().get();
}
public final void setDeparture(final LocalDate departure) {
this.departureProperty().set(departure);
}
public final BooleanProperty confirmedProperty() {
return this.confirmed;
}
public final boolean isConfirmed() {
return this.confirmedProperty().get();
}
public final void setConfirmed(final boolean confirmed) {
this.confirmedProperty().set(confirmed);
}
public final ObjectProperty<Screen> screenProperty() {
return this.screen;
}
public final Screen getScreen() {
return this.screenProperty().get();
}
public final void setScreen(final Screen screen) {
this.screenProperty().set(screen);
}
}
<!-- in confirmation pacakge -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="confirmation.ConfirmationController">
<center>
<VBox spacing="5" alignment="CENTER">
<Label fx:id="arrivalLabel" />
<Label fx:id="departureLabel" />
<Button fx:id="confirmButton" onAction="#confirmOrCancel" />
</VBox>
</center>
<bottom>
<HBox spacing="5" alignment="CENTER">
<Button text="Back" onAction="#goToDeparture" />
</HBox>
</bottom>
</BorderPane>
package confirmation;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import model.BookingModel;
import model.BookingModel.Screen;
public class ConfirmationController {
private final BookingModel model ;
@FXML
private Button confirmButton ;
@FXML
private Label arrivalLabel ;
@FXML
private Label departureLabel ;
public ConfirmationController(BookingModel model) {
this.model = model ;
}
public void initialize() {
confirmButton.textProperty().bind(Bindings
.when(model.confirmedProperty())
.then("Cancel")
.otherwise("Confirm"));
arrivalLabel.textProperty().bind(model.arrivalProperty().asString("Arrival: %s"));
departureLabel.textProperty().bind(model.departureProperty().asString("Departure: %s"));
}
@FXML
private void confirmOrCancel() {
model.setConfirmed(! model.isConfirmed());
}
@FXML
private void goToDeparture() {
model.setScreen(Screen.DEPARTURE);
}
}
package application;
import java.lang.reflect.Constructor;
import javafx.util.Callback;
import model.BookingModel;
public class ControllerFactory implements Callback<Class<?>, Object> {
private final BookingModel model ;
public ControllerFactory(BookingModel model) {
this.model = model ;
}
@Override
public Object call(Class<?> type) {
try {
for (Constructor<?> c : type.getConstructors()) {
if (c.getParameterCount() == 1 && c.getParameterTypes()[0] == BookingModel.class) {
return c.newInstance(model);
}
}
// no appropriate constructor: just use default:
return type.newInstance();
} catch (Exception exc) {
throw new RuntimeException(exc);
}
}
}
<!-- in departure package -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="departure.DepartureController">
<center>
<VBox spacing="5" alignment="CENTER">
<Label fx:id="arrivalLabel" />
<Label text="Pick Departure Date:" />
<DatePicker fx:id="departurePicker"/>
</VBox>
</center>
<bottom>
<HBox spacing="5" alignment="CENTER">
<Button text="Back" onAction="#goToArrival" />
<Button fx:id="nextButton" text="Next" onAction="#goToConfirmation" />
</HBox>
</bottom>
</BorderPane>
package departure;
import java.time.LocalDate;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import model.BookingModel;
public class DepartureController {
private final BookingModel model ;
@FXML
private DatePicker departurePicker ;
@FXML
private Label arrivalLabel ;
@FXML
private Button nextButton ;
public DepartureController(BookingModel model) {
this.model = model ;
}
public void initialize() {
model.setDeparture(null);
departurePicker.setDayCellFactory(dp -> {
DateCell cell = new DateCell() {
private BooleanBinding disableBinding = Bindings.createBooleanBinding(() ->
getItem() == null || model.getArrival() == null || model.getArrival().equals(getItem()) || model.getArrival().isAfter(getItem()),
itemProperty(), model.arrivalProperty());
{
disableBinding.addListener((obs, shouldHaveBeenDisabled, shouldNowBeDisabled) -> setDisable(shouldNowBeDisabled));
}
@Override
public void updateItem(LocalDate date, boolean empty) {
super.updateItem(date, empty);
setDisable(disableBinding.get());
}
};
return cell ;
});
departurePicker.valueProperty().bindBidirectional(model.departureProperty());
departurePicker.disableProperty().bind(model.confirmedProperty());
arrivalLabel.textProperty().bind(model.arrivalProperty().asString("Arrival date: %s"));
nextButton.disableProperty().bind(model.departureProperty().isNull());
}
@FXML
private void goToArrival() {
model.setScreen(BookingModel.Screen.ARRIVAL);
}
@FXML
private void goToConfirmation() {
model.setScreen(BookingModel.Screen.CONFIRMATION);
}
}
application/BookingApplication.java
application/ControllerFactory.java
arrival/Arrival.fxml
arrival/ArrivalController.java
confirmation/Confirmation.fxml
confirmation/ConfirmationController.java
departure/Departure.fxml
departure/DepartureController.java
model/BookingModel.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment