/*
* BSD
* Copyright (c) 2013, Arnaud Nouard
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 the In-SideFX 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 <COPYRIGHT HOLDER> 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 insidefx.undecorator;
import javafx.animation.FadeTransition;
import javafx.animation.FadeTransitionBuilder;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.animation.TranslateTransitionBuilder;
import javafx.application.Platform;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Bounds;
import javafx.geometry.Side;
import javafx.scene.CacheHint;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.Tooltip;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import java.io.IOException;
import java.net.URL;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class, with the UndecoratorController, is the central class for the
* decoration of Transparent Stages. The Stage Undecorator TODO: Themes, manage
* Quit (main stage)
*
* Bugs (Mac only?): Accelerators + Fullscreen crashes JVM KeyCombination does
* not respect keyboard's locale Multi screen: On second screen JFX returns
* wrong value for MinY (300)
*/
public class Undecorator extends StackPane {
private static int SHADOW_WIDTH = 15;
private static int SAVED_SHADOW_WIDTH = 15;
private static int RESIZE_PADDING = 7;
static public int FEEDBACK_STROKE = 4;
public static final Logger LOGGER = Logger.getLogger("Undecorator");
private static ResourceBundle LOC;
private StageStyle stageStyle;
@FXML
private Button menu;
@FXML
private Button close;
@FXML
private Button maximize;
@FXML
private Button minimize;
@FXML
private Button resize;
@FXML
private Button fullscreen;
@FXML
private Label title;
private MenuItem maximizeMenuItem;
private CheckMenuItem fullScreenMenuItem;
private Region clientArea;
private Pane stageDecoration = null;
private Rectangle shadowRectangle;
private Pane glassPane;
private Rectangle dockFeedback;
private FadeTransition dockFadeTransition;
private Stage dockFeedbackPopup;
ParallelTransition parallelTransition;
private DropShadow dsFocused;
private DropShadow dsNotFocused;
private UndecoratorController undecoratorController;
private Stage stage;
private Rectangle resizeRect;
SimpleBooleanProperty maximizeProperty;
private SimpleBooleanProperty minimizeProperty;
private SimpleBooleanProperty closeProperty;
private SimpleBooleanProperty fullscreenProperty;
private final String backgroundStyleClass = "undecorator-background";
private TranslateTransition fullscreenButtonTransition;
public SimpleBooleanProperty maximizeProperty() {
return maximizeProperty;
}
SimpleBooleanProperty minimizeProperty() {
return minimizeProperty;
}
SimpleBooleanProperty closeProperty() {
return closeProperty;
}
public SimpleBooleanProperty fullscreenProperty() {
return fullscreenProperty;
}
public Undecorator(Stage stage, Region root) {
this(stage, root, "stagedecoration.fxml", StageStyle.UNDECORATED);
}
private Undecorator(Stage stag, Region clientArea, String stageDecorationFxml, StageStyle st) {
create(stag, clientArea, getClass().getResource(stageDecorationFxml), st);
}
private Undecorator(Stage stag, Region clientArea, URL stageDecorationFxmlAsURL, StageStyle st) {
create(stag, clientArea, stageDecorationFxmlAsURL, st);
}
void create(Stage stag, Region clientArea, URL stageDecorationFxmlAsURL, StageStyle st) {
this.stage = stag;
this.clientArea = clientArea;
setStageStyle(st);
loadConfig();
// Properties
maximizeProperty = new SimpleBooleanProperty(false);
maximizeProperty.addListener((ov, t, t1) -> getController().maximizeOrRestore());
minimizeProperty = new SimpleBooleanProperty(false);
minimizeProperty.addListener((ov, t, t1) -> getController().minimize());
closeProperty = new SimpleBooleanProperty(false);
closeProperty.addListener((ov, t, t1) -> getController().close());
fullscreenProperty = new SimpleBooleanProperty(false);
fullscreenProperty.addListener((ov, t, t1) -> getController().setFullScreen(!stage.isFullScreen()));
// The controller
undecoratorController = new UndecoratorController(this);
undecoratorController.setAsStageDraggable(stage, clientArea);
// Focus drop shadows: radius, spread, offsets
dsFocused = new DropShadow(BlurType.THREE_PASS_BOX, Color.BLACK, SHADOW_WIDTH, 0.1, 0, 0);
dsNotFocused = new DropShadow(BlurType.THREE_PASS_BOX, Color.DARKGREY, SHADOW_WIDTH, 0, 0, 0);
shadowRectangle = new Rectangle();
// UI part of the decoration
try {
FXMLLoader fxmlLoader = new FXMLLoader(stageDecorationFxmlAsURL);
// fxmlLoader.setController(new StageDecorationController(this));
fxmlLoader.setController(this);
stageDecoration = fxmlLoader.load();
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Decorations not found", ex);
}
initDecoration();
/*
* Resize rectangle
*/
resizeRect = new Rectangle();
resizeRect.setFill(null);
resizeRect.setStrokeWidth(RESIZE_PADDING);
resizeRect.setStrokeType(StrokeType.INSIDE);
resizeRect.setStroke(Color.TRANSPARENT);
undecoratorController.setStageResizableWith(stage, resizeRect, RESIZE_PADDING, SHADOW_WIDTH);
// If not resizable (quick fix)
if (fullscreen != null) {
fullscreen.setVisible(stage.isResizable());
}
resize.setVisible(stage.isResizable());
if (maximize != null) {
maximize.setVisible(stage.isResizable());
}
if (minimize != null && !stage.isResizable()) {
AnchorPane.setRightAnchor(minimize, 34d);
}
// Glass Pane
glassPane = new Pane();
glassPane.setMouseTransparent(true);
buildDockFeedbackStage();
title.getStyleClass().add("undecorator-label-titlebar");
// TODO: how to programmatically get css values? wait for JavaFX 8 custom CSS
shadowRectangle.getStyleClass().add(backgroundStyleClass);
// Do not intercept mouse events on stage's drop shadow
shadowRectangle.setMouseTransparent(true);
// Add all layers
super.getChildren().addAll(shadowRectangle, clientArea, stageDecoration, resizeRect, glassPane);
/*
* Focused stage
*/
stage.focusedProperty().addListener((ov, t, t1) -> setShadowFocused(t1));
/*
* Fullscreen
*/
if (fullscreen != null) {
fullscreen.setOnMouseEntered(t -> {
if (stage.isFullScreen()) {
fullscreen.setOpacity(1);
}
});
fullscreen.setOnMouseExited(t -> {
if (stage.isFullScreen()) {
fullscreen.setOpacity(0.4);
}
});
stage.fullScreenProperty().addListener((ov, t, fullscreenState) -> {
setShadow(!fullscreenState);
fullScreenMenuItem.setSelected(fullscreenState);
maximize.setVisible(!fullscreenState);
minimize.setVisible(!fullscreenState);
resize.setVisible(!fullscreenState);
if (fullscreenState) {
// String and icon
fullscreen.getStyleClass().add("decoration-button-unfullscreen");
fullscreen.setTooltip(new Tooltip(LOC.getString("Restore")));
undecoratorController.saveFullScreenBounds();
if (fullscreenButtonTransition != null) {
fullscreenButtonTransition.stop();
}
// Animate the fullscreen button
fullscreenButtonTransition = TranslateTransitionBuilder.create()
.duration(Duration.millis(3000))
.toX(66)
.node(fullscreen)
.onFinished(t1 -> fullscreenButtonTransition = null)
.build();
fullscreenButtonTransition.play();
fullscreen.setOpacity(0.2);
} else {
// String and icon
fullscreen.getStyleClass().remove("decoration-button-unfullscreen");
fullscreen.setTooltip(new Tooltip(LOC.getString("FullScreen")));
undecoratorController.restoreFullScreenSavedBounds(stage);
fullscreen.setOpacity(1);
if (fullscreenButtonTransition != null) {
fullscreenButtonTransition.stop();
}
// Animate the change
fullscreenButtonTransition = TranslateTransitionBuilder.create()
.duration(Duration.millis(1000))
.toX(0)
.node(fullscreen)
.onFinished(t1 -> fullscreenButtonTransition = null)
.build();
fullscreenButtonTransition.play();
}
});
}
computeAllSizes();
}
/**
* Install default accelerators
*
* @param scene
*/
public void installAccelerators(Scene scene) {
// Accelerators
if (stage.isResizable()) {
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN, KeyCombination.SHORTCUT_DOWN),
this::switchFullscreen);
}
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN), this::switchMinimize);
scene.getAccelerators().put(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN), this::switchClose);
}
/**
* Init the minimum/pref/max size in order to be reflected in the primary
* stage
*/
private void computeAllSizes() {
double minWidth = minWidth(getHeight());
setMinWidth(minWidth);
double minHeight = minHeight(getWidth());
setMinHeight(minHeight);
double prefHeight = prefHeight(getWidth());
setPrefHeight(prefHeight);
double prefWidth = prefWidth(getHeight());
setPrefWidth(prefWidth);
double maxWidth = maxWidth(getHeight());
if (maxWidth > 0) {
setMaxWidth(maxWidth);
}
double maxHeight = maxHeight(getWidth());
if (maxHeight > 0) {
setMaxHeight(maxHeight);
}
}
/*
* The sizing is based on client area's bounds.
*/
@Override
protected double computePrefWidth(double d) {
return clientArea.getPrefWidth() + SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
}
@Override
protected double computePrefHeight(double d) {
return clientArea.getPrefHeight() + SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
}
@Override
protected double computeMaxHeight(double d) {
return clientArea.getMaxHeight() + SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
}
@Override
protected double computeMinHeight(double d) {
double d2 = super.computeMinHeight(d);
d2 += SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
return d2;
}
@Override
protected double computeMaxWidth(double d) {
return clientArea.getMaxWidth() + SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
}
@Override
protected double computeMinWidth(double d) {
double d2 = super.computeMinWidth(d);
d2 += SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
return d2;
}
void setStageStyle(StageStyle st) {
stageStyle = st;
}
public StageStyle getStageStyle() {
return stageStyle;
}
/**
* Activate fade in transition on showing event
*/
public void setFadeInTransition() {
super.setOpacity(0);
stage.showingProperty().addListener((ov, t, t1) -> {
if (t1) {
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(2), Undecorator.this);
fadeTransition.setToValue(1);
fadeTransition.play();
}
});
}
/**
* Launch the fade out transition. Must be invoked when the
* application/window is supposed to be closed
*/
public void setFadeOutTransition() {
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(1), Undecorator.this);
fadeTransition.setToValue(0);
fadeTransition.play();
fadeTransition.setOnFinished(t -> {
stage.hide();
if (dockFeedbackPopup != null && dockFeedbackPopup.isShowing()) {
dockFeedbackPopup.hide();
}
});
}
public void removeDefaultBackgroundStyleClass() {
shadowRectangle.getStyleClass().remove(backgroundStyleClass);
}
public Rectangle getBackgroundNode() {
return shadowRectangle;
}
/**
* Manage buttons and menu items
*/
void initDecoration() {
MenuItem minimizeMenuItem = null;
// Menu
final ContextMenu contextMenu = new ContextMenu();
contextMenu.setAutoHide(true);
if (minimize != null) { // Utility Stage
minimizeMenuItem = new MenuItem(LOC.getString("Minimize"));
minimizeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN));
minimizeMenuItem.setOnAction(e -> switchMinimize());
contextMenu.getItems().add(minimizeMenuItem);
}
if (maximize != null && stage.isResizable()) { // Utility Stage type
maximizeMenuItem = new MenuItem(LOC.getString("Maximize"));
maximizeMenuItem.setOnAction(e -> {
switchMaximize();
contextMenu.hide(); // Stay stuck on screen
});
contextMenu.getItems().addAll(maximizeMenuItem, new SeparatorMenuItem());
}
// Fullscreen
if (stageStyle != StageStyle.UTILITY && stage.isResizable()) {
fullScreenMenuItem = new CheckMenuItem(LOC.getString("FullScreen"));
fullScreenMenuItem.setOnAction(e -> switchFullscreen());
fullScreenMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN, KeyCombination.SHORTCUT_DOWN));
contextMenu.getItems().addAll(fullScreenMenuItem, new SeparatorMenuItem());
}
// Close
MenuItem closeMenuItem = new MenuItem(LOC.getString("Close"));
closeMenuItem.setOnAction(e -> switchClose());
closeMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN));
contextMenu.getItems().add(closeMenuItem);
menu.setOnMousePressed(t -> {
if (contextMenu.isShowing()) {
contextMenu.hide();
} else {
contextMenu.show(menu, Side.BOTTOM, 0, 0);
}
});
// Close button
close.setTooltip(new Tooltip(LOC.getString("Close")));
close.setOnAction(t -> switchClose());
// Maximize button
// If changed via contextual menu
maximizeProperty().addListener((ov, t, t1) -> {
Tooltip tooltip = maximize.getTooltip();
if (tooltip.getText().equals(LOC.getString("Maximize"))) {
tooltip.setText(LOC.getString("Restore"));
maximizeMenuItem.setText(LOC.getString("Restore"));
maximize.getStyleClass().add("decoration-button-restore");
resize.setVisible(false);
} else {
tooltip.setText(LOC.getString("Maximize"));
maximizeMenuItem.setText(LOC.getString("Maximize"));
maximize.getStyleClass().remove("decoration-button-restore");
resize.setVisible(true);
}
});
if (maximize != null) { // Utility Stage
maximize.setTooltip(new Tooltip(LOC.getString("Maximize")));
maximize.setOnAction(t -> switchMaximize());
}
if (fullscreen != null) { // Utility Stage
fullscreen.setTooltip(new Tooltip(LOC.getString("FullScreen")));
fullscreen.setOnAction(t -> switchFullscreen());
}
// Minimize button
if (minimize != null) { // Utility Stage
minimize.setTooltip(new Tooltip(LOC.getString("Minimize")));
minimize.setOnAction(t -> switchMinimize());
}
// Transfer stage title to undecorator tiltle label
title.setText(stage.getTitle());
}
void switchFullscreen() {
// Invoke runLater even if it's on EDT: Crash apps on Mac
Platform.runLater(() -> undecoratorController.setFullScreen(!stage.isFullScreen()));
}
void switchMinimize() {
minimizeProperty().set(!minimizeProperty().get());
}
void switchMaximize() {
maximizeProperty().set(!maximizeProperty().get());
}
void switchClose() {
closeProperty().set(!closeProperty().get());
}
/**
* Bridge to the controller to enable the specified node to drag the stage
*
* @param stage
* @param node
*/
public void setAsStageDraggable(Stage stage, Node node) {
undecoratorController.setAsStageDraggable(stage, node);
}
/**
* Switch the visibility of the window's drop shadow
*/
void setShadow(boolean shadow) {
// Already removed?
if (!shadow && shadowRectangle.getEffect() == null) {
return;
}
// From fullscreen to maximize case
if (shadow && maximizeProperty.get()) {
return;
}
if (!shadow) {
shadowRectangle.setEffect(null);
SAVED_SHADOW_WIDTH = SHADOW_WIDTH;
SHADOW_WIDTH = 0;
} else {
shadowRectangle.setEffect(dsFocused);
SHADOW_WIDTH = SAVED_SHADOW_WIDTH;
}
}
/**
* Set on/off the stage shadow effect
*
* @param b
*/
void setShadowFocused(boolean b) {
if (b) {
shadowRectangle.setEffect(dsFocused);
} else {
shadowRectangle.setEffect(dsNotFocused);
}
}
/**
* Set the layout of different layers of the stage
*/
@Override
public void layoutChildren() {
Bounds b = super.getLayoutBounds();
double w = b.getWidth();
double h = b.getHeight();
ObservableList<Node> list = super.getChildren();
for (Node node : list) {
if (node == shadowRectangle) {
shadowRectangle.setWidth(w - SHADOW_WIDTH * 2);
shadowRectangle.setHeight(h - SHADOW_WIDTH * 2);
shadowRectangle.setX(SHADOW_WIDTH);
shadowRectangle.setY(SHADOW_WIDTH);
} else if (node == stageDecoration) {
stageDecoration.resize(w - SHADOW_WIDTH * 2, h - SHADOW_WIDTH * 2);
stageDecoration.setLayoutX(SHADOW_WIDTH);
stageDecoration.setLayoutY(SHADOW_WIDTH);
} else if (node == resizeRect) {
resizeRect.setWidth(w - SHADOW_WIDTH * 2);
resizeRect.setHeight(h - SHADOW_WIDTH * 2);
resizeRect.setLayoutX(SHADOW_WIDTH);
resizeRect.setLayoutY(SHADOW_WIDTH);
} else {
node.resize(w - SHADOW_WIDTH * 2 - RESIZE_PADDING * 2, h - SHADOW_WIDTH * 2 - RESIZE_PADDING * 2);
node.setLayoutX(SHADOW_WIDTH + RESIZE_PADDING);
node.setLayoutY(SHADOW_WIDTH + RESIZE_PADDING);
}
}
}
public int getShadowBorderSize() {
return SHADOW_WIDTH * 2 + RESIZE_PADDING * 2;
}
UndecoratorController getController() {
return undecoratorController;
}
public Stage getStage() {
return stage;
}
protected Pane getGlassPane() {
return glassPane;
}
public void addGlassPane(Node node) {
glassPane.getChildren().add(node);
}
public void removeGlassPane(Node node) {
glassPane.getChildren().remove(node);
}
/**
* Returns the decoration (buttons...)
*
* @return
*/
public Pane getStageDecorationNode() {
return stageDecoration;
}
/**
* Prepare Stage for dock feedback display
*/
void buildDockFeedbackStage() {
dockFeedbackPopup = new Stage(StageStyle.TRANSPARENT);
dockFeedback = new Rectangle(0, 0, 100, 100);
dockFeedback.setArcHeight(10);
dockFeedback.setArcWidth(10);
dockFeedback.setFill(Color.TRANSPARENT);
dockFeedback.setStroke(Color.BLACK);
dockFeedback.setStrokeWidth(2);
dockFeedback.setCache(true);
dockFeedback.setCacheHint(CacheHint.SPEED);
dockFeedback.setEffect(new DropShadow(BlurType.TWO_PASS_BOX, Color.BLACK, 10, 0.2, 3, 3));
dockFeedback.setMouseTransparent(true);
BorderPane borderpane = new BorderPane();
borderpane.setCenter(dockFeedback);
Scene scene = new Scene(borderpane);
scene.setFill(Color.TRANSPARENT);
dockFeedbackPopup.setScene(scene);
dockFeedbackPopup.sizeToScene();
}
/* void buildDockFeedback() {
` dockFeedbackPopup = new Popup();
dockFeedbackPopup.setHideOnEscape(false);
dockFeedbackPopup.setAutoFix(false);
dockFeedback = new Rectangle(0, 0, 100, 100);
dockFeedback.setFill(Color.TRANSPARENT);
dockFeedback.setStroke(Color.BLACK);
dockFeedback.setStrokeWidth(2);
dockFeedback.setMouseTransparent(true);
// dockFeedback.setStyle("-fx-border-color:black; -fx-border-width:1"); //-fx-background-color: #FFFFFFFF; -fx-background-insets:10;");
dockFeedback.setEffect(new DropShadow(SHADOW_WIDTH, Color.BLACK));
//BorderPane borderpane = new BorderPane();
// borderpane.setCenter(dockFeedback);
dockFeedbackPopup.getContent().add(dockFeedback);
// dockFeedbackPopup.sizeToScene();
}*/
/**
* Activate dock feedback on screen's bounds
*
* @param x
* @param y
*/
public void setDockFeedbackVisible(double x, double y, double width, double height) {
dockFeedbackPopup.setX(x);
dockFeedbackPopup.setY(y);
dockFeedback.setX(SHADOW_WIDTH);
dockFeedback.setY(SHADOW_WIDTH);
dockFeedback.setHeight(height - SHADOW_WIDTH * 2);
dockFeedback.setWidth(width - SHADOW_WIDTH * 2);
dockFeedbackPopup.setWidth(width);
dockFeedbackPopup.setHeight(height);
dockFeedback.setOpacity(1);
dockFeedbackPopup.show();
dockFadeTransition = FadeTransitionBuilder.create()
.duration(Duration.millis(200))
.node(dockFeedback)
.fromValue(0)
.toValue(1)
.autoReverse(true)
.cycleCount(3)
.onFinished(t -> {
//dockFeedback.setVisible(false);
//dockFeedbackPopup.hide();
})
.build();
/*
ScaleTransition scaleTransition = ScaleTransitionBuilder.create()
.duration(Duration.millis(1000))
.node(dockFeedback)
.fromX(0.4)
.fromY(0.4)
.toX(1)
.toY(1)
.build();
TranslateTransition translateTransition = TranslateTransitionBuilder.create()
.duration(Duration.millis(2000))
.node(dockFeedback)
.fromY((height * 0.4) / 2)
.toY(0)
.build();
parallelTransition = new ParallelTransition(dockFeedback);
parallelTransition.getChildren().addAll(dockFadeTransition,scaleTransition);
parallelTransition.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
//dockFeedback.setVisible(false);
dockFeedbackPopup.hide();
}
});
parallelTransition.play();*/
dockFadeTransition.play();
}
public void setDockFeedbackInvisible() {
if (dockFeedbackPopup.isShowing()) {
dockFeedbackPopup.hide();
if (dockFadeTransition != null) {
dockFadeTransition.stop();
}
}
}
private static void loadConfig() {
Properties prop = new Properties();
try {
prop.load(Undecorator.class.getClassLoader().getResourceAsStream("skin/undecorator.properties"));
SHADOW_WIDTH = Integer.parseInt(prop.getProperty("window-shadow-width"));
RESIZE_PADDING = Integer.parseInt(prop.getProperty("window-resize-padding"));
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Error while loading confguration flie", ex);
}
LOC = ResourceBundle.getBundle("insidefx/undecorator/resources/localization", Locale.getDefault());
}
}