package aima.gui.fx.framework; import java.util.ArrayList; import java.util.List; import java.util.Optional; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.canvas.Canvas; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.Separator; import javafx.scene.control.ToolBar; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.text.Font; /** * Builder class for simulation applications. To create a simulation * application, just create a builder, call the define methods to specify what * you need, and then get the result with {@link #getResultFor}. * * @author Ruediger Lunde */ public class SimulationPaneBuilder { protected List<Parameter> parameters = new ArrayList<Parameter>(); protected Optional<Node> stateView = Optional.empty(); /** Should return true if initialization was successful. */ protected Optional<Runnable> initMethod = Optional.empty(); protected Optional<Runnable> simMethod = Optional.empty(); public final void defineParameters(List<Parameter> params) { parameters.clear(); parameters.addAll(params); } public final void defineStateView(Node stateView) { this.stateView = Optional.of(stateView); } public final void defineInitMethod(Runnable initMethod) { this.initMethod = Optional.of(initMethod); } public final void defineSimMethod(Runnable simMethod) { this.simMethod = Optional.of(simMethod); } /** * Adds a toolbar, a state view, and a status label to the provided pane and returns * a controller class instance. The toolbar contains combo boxes to control parameter settings * and buttons for simulation control. The controller class instance handles user events and provides * access to user settings (parameter settings, simulation speed, status text, ...). */ public SimulationPaneCtrl getResultFor(BorderPane pane) { List<ComboBox<String>> combos = new ArrayList<>(); parameters.add(createSimSpeedParam()); for (Parameter param : parameters) { ComboBox<String> combo = new ComboBox<>(); combo.setId(param.getName()); combo.getItems().addAll(param.getValueNames()); combo.getSelectionModel().select(param.getDefaultValueIndex()); combos.add(combo); } Button simBtn = new Button(); Node[] tools = new Node[combos.size() + 2]; for (int i = 0; i < combos.size() - 1; i++) tools[i] = combos.get(i); tools[combos.size() - 1] = new Separator(); tools[combos.size() + 0] = combos.get(combos.size() - 1); tools[combos.size() + 1] = simBtn; ToolBar toolBar = new ToolBar(tools); Label statusLabel = new Label(); statusLabel.setMaxWidth(Double.MAX_VALUE); statusLabel.setAlignment(Pos.CENTER); statusLabel.setFont(Font.font(16)); pane.setTop(toolBar); if (stateView.isPresent()) { if (stateView.get() instanceof Canvas) { // make canvas resizable Canvas canvas = (Canvas) stateView.get(); Pane canvasPane = new Pane(); canvasPane.getChildren().add(canvas); canvas.widthProperty().bind(canvasPane.widthProperty()); canvas.heightProperty().bind(canvasPane.heightProperty()); pane.setCenter(canvasPane); pane.setStyle("-fx-background-color: white"); } else pane.setCenter(stateView.get()); } pane.setBottom(statusLabel); if (!initMethod.isPresent()) throw new IllegalStateException("No initialization method defined."); if (!simMethod.isPresent()) throw new IllegalStateException("No simulation method defined."); return new SimulationPaneCtrl(parameters, combos, initMethod.get(), simMethod.get(), simBtn, statusLabel); } /** * Factory method defining the simulation speed options. Value * <code>Integer.MAX_VALUE</code> is used for pause. */ protected Parameter createSimSpeedParam() { Parameter result = new Parameter(SimulationPaneCtrl.PARAM_SIM_SPEED, 20, 100, 400, 800, Integer.MAX_VALUE); result.setValueNames("VeryFast", "Fast", "Medium", "Slow", "Pause"); result.setDefaultValueIndex(2); return result; } }