package src.com.fxexperience.tools; import java.io.IOException; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.animation.TimelineBuilder; import javafx.application.Application; import javafx.application.Platform; import javafx.beans.binding.ObjectBinding; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXMLLoader; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.SceneBuilder; import javafx.scene.control.ContentDisplay; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; import javafx.scene.effect.ColorAdjust; import javafx.scene.effect.Effect; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.shape.ClosePath; import javafx.scene.shape.LineTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; import javafx.scene.shape.StrokeType; import javafx.scene.text.TextAlignment; import javafx.stage.Stage; import javafx.util.Duration; import src.com.fxexperience.tools.caspianstyler.CaspianStylerMainFrame; import src.com.fxexperience.tools.derivationcalc.DerivationCalcContent; import src.com.fxexperience.tools.splineeditor.SplineEditor; /** * @author Jasper Potts */ public class ToolsApp extends Application { private static final Interpolator interpolator = Interpolator.SPLINE( 0.4829, 0.5709, 0.6803, 0.9928); private static double TOOLBAR_WIDTH = 80; private Pane root; private StackPane currentPane, sparePane; private VBox toolBar; private int currentToolIndex = 0; private Timeline timeline; private Tool nextTool = null; private int nextToolIndex; private DoubleProperty arrowH = new SimpleDoubleProperty(200); private Tool[] tools; public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws IOException { tools = new Tool[] { new Tool( "Caspian Styler", (Parent) FXMLLoader.load(CaspianStylerMainFrame.class .getResource("CaspianStylerMainFrame.fxml")), new Image( ToolsApp.class .getResourceAsStream("images/caspianstyler-icon.png"))), new Tool( "Animation Spline Editor", new SplineEditor(), new Image( ToolsApp.class .getResourceAsStream("images/spline-editor-icon.png"))), new Tool( "Derived Color Calculator", (Parent) FXMLLoader.load(DerivationCalcContent.class .getResource("DerivationCalcContent.fxml")), new Image( ToolsApp.class .getResourceAsStream("images/derive-color-icon.png"))) }; // create root node root = new Pane() { @Override protected void layoutChildren() { double w = getWidth(); double h = getHeight(); toolBar.resizeRelocate(0, 0, TOOLBAR_WIDTH, h); currentPane.relocate(TOOLBAR_WIDTH - 10, 0); currentPane.resize(w - TOOLBAR_WIDTH + 10, h); sparePane.relocate(TOOLBAR_WIDTH - 10, 0); sparePane.resize(w - TOOLBAR_WIDTH + 10, h); Node currentToolButton = toolBar.getChildren().get( currentToolIndex); arrowH.set(currentToolButton.getBoundsInParent().getMinY() + (currentToolButton.getLayoutBounds().getHeight() / 2)); } }; // create toolbar background path Path toolBarBackground = createToolBarPath(null, Color.web("#606060")); // create toolbar toolBar = new VBox(0); toolBar.setId("ToolsToolbar"); toolBar.setClip(createToolBarPath(Color.BLACK, null)); ToggleGroup toggleGroup = new ToggleGroup(); for (int i = 0; i < tools.length; i++) { final int index = i; final Tool tool = tools[i]; final ToggleButton button = new ToggleButton(tool.getName() .replace(' ', '\n')); ImageView icon = new ImageView(tool.getIcon()); icon.effectProperty().bind(new ObjectBinding<Effect>() { { bind(button.selectedProperty()); } @Override protected Effect computeValue() { return button.isSelected() ? null : new ColorAdjust(0, -1, 0, 0); } }); button.setGraphic(icon); button.setContentDisplay(ContentDisplay.TOP); if (i == 0) button.setSelected(true); button.setMaxWidth(Double.MAX_VALUE); button.setAlignment(Pos.CENTER); button.setTextAlignment(TextAlignment.CENTER); button.setToggleGroup(toggleGroup); button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { switchTool(tool, index); } }); toolBar.getChildren().add(button); } currentPane = new StackPane(); currentPane.getChildren().setAll(tools[0].getContent()); sparePane = new StackPane(); sparePane.setVisible(false); root.getChildren().addAll(currentPane, sparePane, toolBarBackground, toolBar); primaryStage.setTitle("FX Experience Tools"); primaryStage .setScene(SceneBuilder .create() .root(root) .stylesheets( ToolsApp.class.getResource("Tools.css") .toString()).build()); primaryStage.show(); } private Path createToolBarPath(Paint fill, Paint stroke) { Path toolBarBackground = new Path(); toolBarBackground.setFill(fill); toolBarBackground.setStroke(stroke); toolBarBackground.setStrokeType(StrokeType.OUTSIDE); LineTo arrowTop = new LineTo(TOOLBAR_WIDTH, 0); arrowTop.yProperty().bind(arrowH.add(-8)); LineTo arrowTip = new LineTo(TOOLBAR_WIDTH - 10, 0); arrowTip.yProperty().bind(arrowH); LineTo arrowBottom = new LineTo(TOOLBAR_WIDTH, 0); arrowBottom.yProperty().bind(arrowH.add(8)); LineTo bottomRight = new LineTo(TOOLBAR_WIDTH, 0); bottomRight.yProperty().bind(root.heightProperty()); LineTo bottomLeft = new LineTo(0, 0); bottomLeft.yProperty().bind(root.heightProperty()); toolBarBackground.getElements().addAll(new MoveTo(0, 0), new LineTo(TOOLBAR_WIDTH, 0), arrowTop, arrowTip, arrowBottom, bottomRight, bottomLeft, new ClosePath()); return toolBarBackground; } public void switchTool(Tool newTool, final int toolIndex) { // check if existing animation running if (timeline != null) { nextTool = newTool; nextToolIndex = toolIndex; timeline.setRate(4); return; } else { nextTool = null; nextToolIndex = -1; } // stop any animations if (tools[currentToolIndex].getContent() instanceof AnimatedPanel) { ((AnimatedPanel) tools[currentToolIndex].getContent()) .stopAnimations(); } // load new content sparePane.getChildren().setAll(newTool.getContent()); sparePane.setCache(true); currentPane.setCache(true); // wait one pulse then animate Platform.runLater(new Runnable() { @Override public void run() { // animate switch if (toolIndex > currentToolIndex) { // animate from bottom currentToolIndex = toolIndex; sparePane.setTranslateY(root.getHeight()); sparePane.setVisible(true); timeline = TimelineBuilder .create() .keyFrames( new KeyFrame(Duration.millis(0), new KeyValue(currentPane .translateYProperty(), 0, interpolator), new KeyValue(sparePane .translateYProperty(), root .getHeight(), interpolator)), new KeyFrame(Duration.millis(800), animationEndEventHandler, new KeyValue(currentPane .translateYProperty(), -root.getHeight(), interpolator), new KeyValue(sparePane .translateYProperty(), 0, interpolator))).build(); timeline.play(); } else { // from top currentToolIndex = toolIndex; sparePane.setTranslateY(-root.getHeight()); sparePane.setVisible(true); timeline = TimelineBuilder .create() .keyFrames( new KeyFrame(Duration.millis(0), new KeyValue(currentPane .translateYProperty(), 0, interpolator), new KeyValue(sparePane .translateYProperty(), -root.getHeight(), interpolator)), new KeyFrame( Duration.millis(800), animationEndEventHandler, new KeyValue(currentPane .translateYProperty(), root .getHeight(), interpolator), new KeyValue(sparePane .translateYProperty(), 0, interpolator))).build(); timeline.play(); } } }); } private EventHandler<ActionEvent> animationEndEventHandler = new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { // switch panes StackPane temp = currentPane; currentPane = sparePane; sparePane = temp; // cleanup timeline = null; currentPane.setTranslateY(0); sparePane.setCache(false); currentPane.setCache(false); sparePane.setVisible(false); sparePane.getChildren().clear(); // start any animations if (tools[currentToolIndex].getContent() instanceof AnimatedPanel) { ((AnimatedPanel) tools[currentToolIndex].getContent()) .startAnimations(); } // check if we have a animation waiting if (nextTool != null) { switchTool(nextTool, nextToolIndex); } } }; }