/** * AbstractPopoutClient.java * * Copyright (c) 2013-2016, F(X)yz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of F(X)yz, any associated website, nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL F(X)yz BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.fxyz3d.ExtrasAndTests; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import javafx.util.Duration; /** * * @author Jason Pollastrini aka jdub1581 */ public abstract class AbstractPopoutClient extends AnchorPane { @FXML protected StackPane topPane; @FXML protected StackPane menuPane; @FXML protected HBox centerPane; @FXML protected StackPane center; @FXML protected StackPane right; @FXML protected StackPane bottomPane; @FXML protected Pane menuPaneTrigger; @FXML protected Pane bottomPaneTrigger; @FXML protected Pane topPaneTrigger; protected Stage stage; private AbstractPopoutClient() { try { FXMLLoader ldr = getUILoader(); ldr.setController(this); ldr.setRoot(this); ldr.load(); } catch (IOException ex) { Logger.getLogger(SimpleSamplerClient.class.getName()).log(Level.SEVERE, null, ex); } } public AbstractPopoutClient(Stage stage, boolean popsEnabled) { this(); if (popsEnabled) { initialize(); } this.stage = stage; } private void initialize() { menuPaneLocation.addListener(it -> updateMenuPaneAnchors()); bottomPaneLocation.addListener(it -> updateBottomPaneAnchors()); showMenuPaneProperty().addListener(it -> animateMenuPane()); showBottomPaneProperty().addListener(it -> animateBottomPane()); menuPane.setOnMouseExited(evt -> { if (menuPane.getOpacity() == 1) { setShowMenuPane(false); menuPaneTrigger.toFront(); menuPaneTrigger.setVisible(true); } }); menuPaneTrigger.setOnMouseEntered(e -> { if (menuPane.getOpacity() != 1) { setShowMenuPane(true); menuPaneTrigger.toBack(); menuPaneTrigger.setVisible(false); } }); bottomPaneTrigger.setOnMouseEntered(e -> { if (bottomPane.getOpacity() != 1) { setShowBottomPane(true); bottomPaneTrigger.toBack(); bottomPaneTrigger.setVisible(false); } }); bottomPane.setOnMouseClicked(evt -> { if (bottomPane.getOpacity() == 1) { setShowBottomPane(false); bottomPaneTrigger.toFront(); bottomPaneTrigger.setVisible(true); } }); } private final BooleanProperty showMenuPane = new SimpleBooleanProperty(this, "showMenuPane", true); public final boolean isShowMenuPane() { return showMenuPane.get(); } public final void setShowMenuPane(boolean showMenu) { showMenuPane.set(showMenu); } /** * Returns the property used to control the visibility of the menu panel. * When the value of this property changes to false then the menu panel will * slide out to the left). * * @return the property used to control the menu panel */ public final BooleanProperty showMenuPaneProperty() { return showMenuPane; } private final BooleanProperty showBottomPane = new SimpleBooleanProperty(this, "showBottomPane", true); public final boolean isShowBottomPane() { return showBottomPane.get(); } public final void setShowBottomPane(boolean showBottom) { showBottomPane.set(showBottom); } /** * Returns the property used to control the visibility of the bottom panel. * When the value of this property changes to false then the bottom panel * will slide out to the left). * * @return the property used to control the bottom panel */ public final BooleanProperty showBottomPaneProperty() { return showBottomPane; } /* * The updateMenu/BottomPaneAnchors methods get called whenever the value of * menuPaneLocation or bottomPaneLocation changes. Setting anchor pane * constraints will automatically trigger a relayout of the anchor pane * children. */ private void updateMenuPaneAnchors() { setLeftAnchor(menuPane, getMenuPaneLocation()); setLeftAnchor(centerPane, getMenuPaneLocation() + menuPane.getWidth()); } private void updateBottomPaneAnchors() { setBottomAnchor(bottomPane, getBottomPaneLocation()); setBottomAnchor(centerPane, getBottomPaneLocation() + bottomPane.getHeight()); setBottomAnchor(menuPane, getBottomPaneLocation() + bottomPane.getHeight()); } /* * Starts the animation for the menu pane. */ private void animateMenuPane() { if (isShowMenuPane()) { slideMenuPane(0); } else { slideMenuPane(-menuPane.prefWidth(-1)); } } /* * Starts the animation for the bottom pane. */ private void animateBottomPane() { if (isShowBottomPane()) { slideBottomPane(0); } else { slideBottomPane(-bottomPane.prefHeight(-1)); } } /* * The animations are using the JavaFX timeline concept. The timeline updates * properties. In this case we have to introduce our own properties further * below (menuPaneLocation, bottomPaneLocation) because ultimately we need to * update layout constraints, which are not properties. So this is a little * work-around. */ private void slideMenuPane(double toX) { KeyValue keyValue = new KeyValue(menuPaneLocation, toX, Interpolator.EASE_BOTH); KeyValue visible = new KeyValue(menuPane.opacityProperty(), menuPane.getOpacity() == 1 ? 0 : 1); KeyFrame keyFrame = new KeyFrame(Duration.millis(600), keyValue, visible); Timeline timeline = new Timeline(keyFrame); timeline.play(); } private void slideBottomPane(double toY) { KeyValue keyValue = new KeyValue(bottomPaneLocation, toY, Interpolator.EASE_BOTH); KeyValue visible = new KeyValue(bottomPane.opacityProperty(), bottomPane.getOpacity() == 1 ? 0 : 1); KeyFrame keyFrame = new KeyFrame(Duration.millis(600), keyValue, visible); Timeline timeline = new Timeline(keyFrame); timeline.play(); } private DoubleProperty menuPaneLocation = new SimpleDoubleProperty(this, "menuPaneLocation"); private double getMenuPaneLocation() { return menuPaneLocation.get(); } private DoubleProperty bottomPaneLocation = new SimpleDoubleProperty(this, "bottomPaneLocation"); private double getBottomPaneLocation() { return bottomPaneLocation.get(); } protected String getFXMLPath() { return "Client_Slide_Outs.fxml"; } private FXMLLoader getUILoader() { return new FXMLLoader(getClass().getResource(getFXMLPath())); } public StackPane getTopPane() { return topPane; } public StackPane getMenuPane() { return menuPane; } public StackPane getCenter() { return center; } public StackPane getRight() { return right; } public StackPane getBottomPane() { return bottomPane; } public Pane getBottomPaneTrigger() { return bottomPaneTrigger; } }