package aima.gui.fx.framework; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.DoubleProperty; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.Menu; import javafx.scene.control.MenuBar; import javafx.scene.control.MenuItem; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.text.Font; import javafx.stage.Stage; /** * Builder for integrated applications. To create an integrated application, * just create a builder, define a title, define scene size, register apps (integrable JavaFX * applications) and demos (command line applications), and finally get the result from the * builder and show it. * * @author Ruediger Lunde */ public class IntegratedAppBuilder { private MenuBar menuBar = new MenuBar(); private Menu appsMenu = new Menu("Apps"); private Menu demosMenu = new Menu("Demos"); private String title = ""; private double sceneWidth = 1200; private double sceneHeight = 800; private IntegratedAppPaneCtrl paneCtrl; public IntegratedAppBuilder() { paneCtrl = new IntegratedAppPaneCtrl(); final DoubleProperty scale = paneCtrl.scaleProperty(); MenuItem incScaleItem = new MenuItem("Inc Scale"); incScaleItem.setOnAction(ev -> scale.set(trunc(scale.get() * 1.3))); incScaleItem.setAccelerator(new KeyCodeCombination(KeyCode.PLUS, KeyCombination.CONTROL_DOWN)); MenuItem decScaleItem = new MenuItem("Dec Scale"); decScaleItem.setOnAction(ev -> scale.set(trunc(scale.get() / 1.3))); decScaleItem.setAccelerator(new KeyCodeCombination(KeyCode.MINUS, KeyCombination.CONTROL_DOWN)); MenuItem exitItem = new MenuItem("Exit"); exitItem.setOnAction(ev -> Platform.exit()); Menu fileMenu = new Menu("File"); fileMenu.getItems().addAll(incScaleItem, decScaleItem, exitItem); menuBar.getMenus().addAll(fileMenu, appsMenu, demosMenu); menuBar.styleProperty().bind(Bindings.concat("-fx-font-size: ", paneCtrl.scaleProperty().multiply(Font.getDefault().getSize()).asString())); } public void defineTitle(String title) { this.title = title; } public void registerApp(Class<? extends IntegrableApplication> appClass) { final IntegratedAppPaneCtrl ctrl = paneCtrl; MenuItem item = new MenuItem(appClass.getSimpleName()); item.setOnAction(ev -> ctrl.startApp(appClass)); addToMenu(appsMenu, appClass.getPackage().getName(), item); } public void registerDemo(Class<?> demoClass) { final IntegratedAppPaneCtrl ctrl = paneCtrl; MenuItem item = new MenuItem(demoClass.getSimpleName()); item.setOnAction(ev -> ctrl.startDemo(demoClass)); addToMenu(demosMenu, demoClass.getPackage().getName(), item); } public void defineSceneSize(double width, double height) { sceneWidth = width; sceneHeight = height; } /** * Creates a scene with menu bar and a scalable container pane, * assigns it to the stage, and returns a controller instance containing * user interface logic. * @return A controller class (will seldom be used by the caller). */ public IntegratedAppPaneCtrl getResultFor(Stage stage) { // create a pane, content is affected by scale final DoubleProperty scale = paneCtrl.scaleProperty(); BorderPane appPane = new BorderPane(); appPane.scaleXProperty().bind(scale); appPane.scaleYProperty().bind(scale); Pane appPaneContainer = new Pane(); appPaneContainer.getChildren().add(appPane); appPane.prefWidthProperty().bind(appPaneContainer.widthProperty().divide(scale)); appPane.prefHeightProperty().bind(appPaneContainer.heightProperty().divide(scale)); appPane.translateXProperty() .bind(appPaneContainer.widthProperty().subtract(appPane.prefWidthProperty()).divide(2)); appPane.translateYProperty() .bind(appPaneContainer.heightProperty().subtract(appPane.prefHeightProperty()).divide(2)); paneCtrl.setContext(appPane, stage, title); BorderPane root = new BorderPane(); root.setTop(menuBar); root.setCenter(appPaneContainer); stage.setScene(new Scene(root, sceneWidth, sceneHeight)); // just in case, the builder is called twice... IntegratedAppPaneCtrl result = paneCtrl; paneCtrl = new IntegratedAppPaneCtrl(); return result; } /** * Adds a new starter item to the specified menu. */ private MenuItem addToMenu(Menu menu, String packageName, MenuItem item) { Menu subMenu = null; ObservableList<MenuItem> menuComps = menu.getItems(); int i; for (i = 0; i < menuComps.size(); i++) { Menu comp = (Menu) menuComps.get(i); if (comp.getText().equals(packageName)) subMenu = comp; else if (comp.getText().compareTo(packageName) > 0) break; } if (subMenu == null) { subMenu = new Menu(packageName); menu.getItems().add(i, subMenu); } subMenu.getItems().add(item); return item; } private double trunc(double num) { return Math.round(num * 2) / 2.0; } }