/* * Copyright (c) 2013 by Gerrit Grunwald * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package eu.hansolo.enzo.splitflap.skin; import eu.hansolo.enzo.common.ShapeConverter; import eu.hansolo.enzo.common.Util; import eu.hansolo.enzo.splitflap.FlipEvent; import eu.hansolo.enzo.splitflap.SplitFlap; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.VPos; import javafx.scene.CacheHint; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; import javafx.scene.effect.BlurType; import javafx.scene.effect.InnerShadow; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.scene.paint.Color; import javafx.scene.paint.CycleMethod; import javafx.scene.paint.LinearGradient; import javafx.scene.paint.Stop; import javafx.scene.shape.ClosePath; import javafx.scene.shape.FillRule; import javafx.scene.shape.LineTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; import javafx.scene.text.Font; import javafx.scene.text.TextAlignment; import javafx.scene.transform.Rotate; import javafx.util.Duration; import java.util.ArrayList; public class SplitFlapSkin extends SkinBase<SplitFlap> implements Skin<SplitFlap> { private static final double PREFERRED_WIDTH = 112; private static final double PREFERRED_HEIGHT = 189; private static final double MINIMUM_WIDTH = 5; private static final double MINIMUM_HEIGHT = 5; private static final double MAXIMUM_WIDTH = 1024; private static final double MAXIMUM_HEIGHT = 1024; private static double aspectRatio; private final FlipEvent FLIP_FINISHED; private ArrayList<String> selectedSet; private int currentSelectionIndex; private int nextSelectionIndex; private double width; private double height; private double flapHeight; private Pane pane; private Region fixtureRight; private Region fixtureLeft; private InnerShadow innerShadow; private InnerShadow innerHighlight; private InnerShadow reversedInnerShadow; private InnerShadow reversedInnerHighlight; private Region upperBackground; private Canvas upperBackgroundText; private GraphicsContext ctxUpperBackgroundText; private Region lowerBackground; private Canvas lowerBackgroundText; private GraphicsContext ctxLowerBackgroundText; private Region flap; private Canvas flapTextFront; private GraphicsContext ctxTextFront; private Canvas flapTextBack; private GraphicsContext ctxTextBack; private LinearGradient upperTextFill; private LinearGradient lowerTextFill; private Font font; private Rotate rotateFlap; private Timeline timeline; private KeyFrame keyFrame; private KeyValue keyValueFlap; // ******************** Constructors ************************************** public SplitFlapSkin(final SplitFlap CONTROL) { super(CONTROL); FLIP_FINISHED = new FlipEvent(this, getSkinnable(), FlipEvent.FLIP_FINISHED); selectedSet = getSkinnable().getSelectedSet(); currentSelectionIndex = getSkinnable().getSelectedSet().indexOf(getSkinnable().getText()); nextSelectionIndex = currentSelectionIndex + 1 > getSkinnable().getSelectedSet().size() ? 0 : currentSelectionIndex + 1; aspectRatio = PREFERRED_HEIGHT / PREFERRED_WIDTH; pane = new Pane(); rotateFlap = new Rotate(); rotateFlap.setAxis(Rotate.X_AXIS); rotateFlap.setAngle(0); flapHeight = 0.49206349206349204 * PREFERRED_HEIGHT; timeline = new Timeline(); init(); initGraphics(); registerListeners(); } // ******************** Initialization ************************************ private void init() { if (Double.compare(getSkinnable().getPrefWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getPrefHeight(), 0.0) <= 0 || Double.compare(getSkinnable().getWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getHeight(), 0.0) <= 0) { if (getSkinnable().getPrefWidth() > 0 && getSkinnable().getPrefHeight() > 0) { getSkinnable().setPrefSize(getSkinnable().getPrefWidth(), getSkinnable().getPrefHeight()); } else { getSkinnable().setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT); } } if (Double.compare(getSkinnable().getMinWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getMinHeight(), 0.0) <= 0) { getSkinnable().setMinSize(MINIMUM_WIDTH, MINIMUM_HEIGHT); } if (Double.compare(getSkinnable().getMaxWidth(), 0.0) <= 0 || Double.compare(getSkinnable().getMaxHeight(), 0.0) <= 0) { getSkinnable().setMaxSize(MAXIMUM_WIDTH, MAXIMUM_HEIGHT); } if (getSkinnable().getPrefWidth() != PREFERRED_WIDTH || getSkinnable().getPrefHeight() != PREFERRED_HEIGHT) { aspectRatio = getSkinnable().getPrefHeight() / getSkinnable().getPrefWidth(); } selectedSet.addAll(getSkinnable().getSelectedSet()); } private void initGraphics() { fixtureRight = new Region(); fixtureRight.getStyleClass().setAll(getSkinnable().isDarkFixture() ? "fixture-dark" : "fixture"); fixtureRight.setOpacity(getSkinnable().isWithFixture() ? 1 : 0); fixtureLeft = new Region(); fixtureLeft.getStyleClass().setAll(getSkinnable().isDarkFixture() ? "fixture-dark" : "fixture"); fixtureLeft.setOpacity(getSkinnable().isWithFixture() ? 1 : 0); innerShadow = new InnerShadow(); innerShadow.setOffsetY(-0.01 * flapHeight); innerShadow.setRadius(0.01 * flapHeight); innerShadow.setColor(Color.rgb(0, 0, 0, 0.65)); innerShadow.setBlurType(BlurType.TWO_PASS_BOX); innerHighlight = new InnerShadow(); innerHighlight.setOffsetY(0.01 * flapHeight); innerHighlight.setRadius(0.01 * flapHeight); innerHighlight.setColor(Color.rgb(255, 255, 255, 0.65)); innerHighlight.setBlurType(BlurType.TWO_PASS_BOX); innerHighlight.setInput(innerShadow); reversedInnerShadow = new InnerShadow(); reversedInnerShadow.setOffsetY(-0.01 * 0.4920634921 * height); reversedInnerShadow.setRadius(0.01 * 0.4920634921 * height); reversedInnerShadow.setColor(Color.rgb(0, 0, 0, 0.65)); reversedInnerShadow.setBlurType(BlurType.TWO_PASS_BOX); reversedInnerHighlight = new InnerShadow(); reversedInnerHighlight.setOffsetY(0.01 * 0.4920634921 * height); reversedInnerHighlight.setRadius(0.01 * 0.4920634921 * height); reversedInnerHighlight.setColor(Color.rgb(255, 255, 255, 0.65)); reversedInnerHighlight.setBlurType(BlurType.TWO_PASS_BOX); reversedInnerHighlight.setInput(innerShadow); getSkinnable().setStyle("-flap-base: " + Util.colorToCss(getSkinnable().getFlapColor()) + ";"); upperBackground = new Region(); upperBackground.setEffect(innerHighlight); //font = Font.font("Bebas Neue", PREFERRED_HEIGHT); //font = Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/bebasneue.otf"), PREFERRED_HEIGHT); font = Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/droidsansmono.ttf"), PREFERRED_HEIGHT); upperTextFill = new LinearGradient(0, 0, 0, flapHeight, false, CycleMethod.NO_CYCLE, new Stop(0.0, getSkinnable().getTextColor()), new Stop(1.0, getSkinnable().getTextColor().darker())); upperBackgroundText = new Canvas(); ctxUpperBackgroundText = upperBackgroundText.getGraphicsContext2D(); ctxUpperBackgroundText.setTextBaseline(VPos.CENTER); ctxUpperBackgroundText.setTextAlign(TextAlignment.CENTER); lowerBackground = new Region(); lowerBackground.setEffect(innerHighlight); lowerTextFill = new LinearGradient(0, 0.5079365079365079 * PREFERRED_HEIGHT, 0, 0.5079365079365079 * PREFERRED_HEIGHT + flapHeight, false, CycleMethod.NO_CYCLE, new Stop(0.0, getSkinnable().getTextColor().darker()), new Stop(1.0, getSkinnable().getTextColor())); lowerBackgroundText = new Canvas(); ctxLowerBackgroundText = lowerBackgroundText.getGraphicsContext2D(); ctxLowerBackgroundText.setTextBaseline(VPos.CENTER); ctxLowerBackgroundText.setTextAlign(TextAlignment.CENTER); flap = new Region(); flap.setEffect(innerHighlight); flap.getTransforms().add(rotateFlap); flapTextFront = new Canvas(); flapTextFront.getTransforms().add(rotateFlap); ctxTextFront = flapTextFront.getGraphicsContext2D(); ctxTextFront.setTextBaseline(VPos.CENTER); ctxTextFront.setTextAlign(TextAlignment.CENTER); flapTextBack = new Canvas(); flapTextBack.getTransforms().add(rotateFlap); flapTextBack.setOpacity(0); ctxTextBack = flapTextBack.getGraphicsContext2D(); ctxTextBack.setTextBaseline(VPos.CENTER); ctxTextBack.setTextAlign(TextAlignment.CENTER); // Set the appropriate style class for the flaps if (getSkinnable().isWithFixture()) { upperBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-square" : "upper"); lowerBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "lower-square" : "lower"); flap.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-square" : "upper"); } else { upperBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-no-fixture-square" : "upper-no-fixture"); lowerBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "lower-no-fixture-square" : "lower-no-fixture"); flap.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-no-fixture-square" : "upper-no-fixture"); } pane.getChildren().setAll(fixtureRight, fixtureLeft, upperBackground, lowerBackground, upperBackgroundText, lowerBackgroundText, flap, flapTextFront, flapTextBack); getChildren().setAll(pane); resize(); } private void registerListeners() { getSkinnable().widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") ); getSkinnable().heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") ); getSkinnable().prefWidthProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE") ); getSkinnable().prefHeightProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE") ); getSkinnable().textProperty().addListener(observable -> handleControlPropertyChanged("TEXT") ); getSkinnable().flapColorProperty().addListener(observable -> handleControlPropertyChanged("FLAP_COLOR") ); getSkinnable().textColorProperty().addListener(observable -> handleControlPropertyChanged("TEXT_COLOR") ); getSkinnable().withFixtureProperty().addListener(observable -> handleControlPropertyChanged("WITH_FIXTURE") ); getSkinnable().darkFixtureProperty().addListener(observable -> handleControlPropertyChanged("DARK_FIXTURE") ); getSkinnable().squareFlapsProperty().addListener(observable -> handleControlPropertyChanged("SQUARE_FLAPS") ); getSkinnable().getStyleClass().addListener(new ListChangeListener<String>() { @Override public void onChanged(Change<? extends String> change) { resize(); } }); rotateFlap.angleProperty().addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> ov, Number oldAngle, Number newAngle) { if (newAngle.doubleValue() > 90) { flapTextFront.setOpacity(0); flapTextBack.setOpacity(1); flap.setEffect(reversedInnerHighlight); } if (newAngle.doubleValue() < 90) { // frontside text visible = true } } }); timeline.setOnFinished(new EventHandler<ActionEvent>() { @Override public void handle(final ActionEvent EVENT) { getSkinnable().fireEvent(FLIP_FINISHED); flap.setCache(false); flap.setCacheShape(false); if (Double.compare(rotateFlap.getAngle(), 180) == 0) { flap.setEffect(innerHighlight); rotateFlap.setAngle(0); flapTextBack.setOpacity(0); flapTextFront.setOpacity(1); refreshTextCtx(); if (!getSkinnable().getText().equals(selectedSet.get(currentSelectionIndex))) { flipForward(); } } else if(Double.compare(rotateFlap.getAngle(), 0) == 0) { rotateFlap.setAngle(180); } } }); } // ******************** Methods ******************************************* protected void handleControlPropertyChanged(final String PROPERTY) { if ("RESIZE".equals(PROPERTY)) { resize(); } else if ("PREF_SIZE".equals(PROPERTY)) { aspectRatio = getSkinnable().getPrefHeight() / getSkinnable().getPrefWidth(); } else if ("TEXT".equals(PROPERTY)) { flipForward(); } else if ("FLAP_COLOR".equals(PROPERTY)) { getSkinnable().setStyle("-flap-base: " + Util.colorToCss(getSkinnable().getFlapColor()) + ";"); } else if ("TEXT_COLOR".equals(PROPERTY)) { refreshTextCtx(); } else if ("CHARACTER_SET".equals(PROPERTY)) { selectedSet.clear(); for (String text : getSkinnable().getSelectedSet()) { selectedSet.add(text); } } else if ("WITH_FIXTURE".equals(PROPERTY)) { fixtureLeft.setOpacity(getSkinnable().isWithFixture() ? 1 : 0); fixtureRight.setOpacity(getSkinnable().isWithFixture() ? 1 : 0); if (!getSkinnable().isWithFixture()) { upperBackground.getStyleClass().setAll("upper-no-fixture"); lowerBackground.getStyleClass().setAll("lower-no-fixture"); flap.getStyleClass().setAll("upper-no-fixture"); } } else if ("DARK_FIXTURE".equals(PROPERTY)) { fixtureLeft.getStyleClass().setAll(getSkinnable().isDarkFixture() ? "fixture-left-dark" : "fixture-left"); fixtureRight.getStyleClass().setAll(getSkinnable().isDarkFixture() ? "fixture-right-dark" : "fixture-right"); } else if ("SQUARE_FLAPS".equals(PROPERTY)) { if (getSkinnable().isWithFixture()) { upperBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-square" : "upper"); lowerBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "lower-square" : "lower"); flap.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-square" : "upper"); } else { upperBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-no-fixture-square" : "upper-no-fixture"); lowerBackground.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "lower-no-fixture-square" : "lower-no-fixture"); flap.getStyleClass().setAll(getSkinnable().isSquareFlaps() ? "upper-no-fixture-square" : "upper-no-fixture"); } } } @Override protected double computeMinWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) { return super.computeMinWidth(Math.max(MINIMUM_HEIGHT, HEIGHT - TOP_INSET - BOTTOM_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET); } @Override protected double computeMinHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) { return super.computeMinHeight(Math.max(MINIMUM_WIDTH, WIDTH - LEFT_INSET - RIGHT_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET); } @Override protected double computeMaxWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) { return super.computeMaxWidth(Math.min(MAXIMUM_HEIGHT, HEIGHT - TOP_INSET - BOTTOM_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET); } @Override protected double computeMaxHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) { return super.computeMaxHeight(Math.min(MAXIMUM_WIDTH, WIDTH - LEFT_INSET - RIGHT_INSET), TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET); } @Override protected double computePrefWidth(final double HEIGHT, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) { double prefHeight = PREFERRED_HEIGHT; if (HEIGHT != -1) { prefHeight = Math.max(0, PREFERRED_HEIGHT - TOP_INSET - BOTTOM_INSET); } return super.computePrefWidth(prefHeight, TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET); } @Override protected double computePrefHeight(final double WIDTH, double TOP_INSET, double RIGHT_INSET, double BOTTOM_INSET, double LEFT_INSET) { double prefWidth = PREFERRED_WIDTH; if (WIDTH != -1) { prefWidth = Math.max(0, WIDTH - LEFT_INSET - RIGHT_INSET); } return super.computePrefHeight(prefWidth, TOP_INSET, RIGHT_INSET, BOTTOM_INSET, LEFT_INSET); } public void flipForward() { timeline.stop(); flap.setCacheShape(true); flap.setCache(true); flap.setCacheHint(CacheHint.ROTATE); //flap.setCacheHint(CacheHint.SPEED); currentSelectionIndex++; if (currentSelectionIndex >= selectedSet.size()) { currentSelectionIndex = 0; } nextSelectionIndex = currentSelectionIndex + 1; if (nextSelectionIndex >= selectedSet.size()) { nextSelectionIndex = 0; } //keyValueFlap = new KeyValue(rotateFlap.angleProperty(), 180, Interpolator.SPLINE(0.5, 0.4, 0.4, 1.0)); keyValueFlap = new KeyValue(rotateFlap.angleProperty(), 180, Interpolator.EASE_IN); keyFrame = new KeyFrame(Duration.millis(getSkinnable().getFlipTime()), keyValueFlap); timeline.getKeyFrames().setAll(keyFrame); timeline.play(); } public void flipBackward() { timeline.stop(); //keyValueFlap = new KeyValue(rotateFlap.angleProperty(), -180, Interpolator.SPLINE(0.5, 0.4, 0.4, 1.0)); keyValueFlap = new KeyValue(rotateFlap.angleProperty(), -180, Interpolator.EASE_IN); keyFrame = new KeyFrame(Duration.millis(getSkinnable().getFlipTime()), keyValueFlap); timeline.getKeyFrames().setAll(keyFrame); timeline.play(); } private void refreshTextCtx() { double flapWidth = flapTextFront.getWidth(); double flapHeight = flapTextFront.getHeight(); upperTextFill = new LinearGradient(0, 0, 0, flapHeight, false, CycleMethod.NO_CYCLE, new Stop(0.0, getSkinnable().getTextColor().brighter().brighter()), new Stop(0.99, getSkinnable().getTextColor()), new Stop(1.0, getSkinnable().getTextColor().darker())); lowerTextFill = new LinearGradient(0, 0, 0, flapHeight, false, CycleMethod.NO_CYCLE, new Stop(0.0, getSkinnable().getTextColor().brighter().brighter()), new Stop(0.01, getSkinnable().getTextColor().brighter()), new Stop(1.0, getSkinnable().getTextColor())); // set the text on the upper background ctxUpperBackgroundText.clearRect(0, 0, flapWidth, flapHeight); ctxUpperBackgroundText.setFill(upperTextFill); //ctxUpperBackgroundText.fillText(selectedSet.get(nextSelectionIndex), width * 0.5, height * 0.55); ctxUpperBackgroundText.fillText(selectedSet.get(nextSelectionIndex), width * 0.5, height * 0.5); // set the text on the lower background ctxLowerBackgroundText.clearRect(0, 0, flapWidth, flapHeight); ctxLowerBackgroundText.setFill(lowerTextFill); //ctxLowerBackgroundText.fillText(selectedSet.get(currentSelectionIndex), width * 0.5, height * 0.041); ctxLowerBackgroundText.fillText(selectedSet.get(currentSelectionIndex), width * 0.5, 0); // set the text on the flap front ctxTextFront.clearRect(0, 0, flapWidth, flapHeight); ctxTextFront.setFill(upperTextFill); //ctxTextFront.fillText(selectedSet.get(currentSelectionIndex), width * 0.5, height * 0.55); ctxTextFront.fillText(selectedSet.get(currentSelectionIndex), width * 0.5, height * 0.5); // set the text on the flap back ctxTextBack.clearRect(0, 0, flapWidth, flapHeight); ctxTextBack.setFill(new LinearGradient(0, 0, 0, -flapHeight, false, CycleMethod.NO_CYCLE, new Stop(0.0, getSkinnable().getTextColor().brighter().brighter()), new Stop(0.99, getSkinnable().getTextColor().brighter()), new Stop(1.0, getSkinnable().getTextColor()))); ctxTextBack.save(); ctxTextBack.scale(1,-1); //ctxTextBack.fillText(selectedSet.get(nextSelectionIndex), width * 0.5, -height * 0.45); ctxTextBack.fillText(selectedSet.get(nextSelectionIndex), width * 0.5, -height * 0.5); ctxTextBack.restore(); } // ******************** Resizing ****************************************** private void resize() { width = getSkinnable().getWidth(); height = getSkinnable().getHeight(); if (getSkinnable().isKeepAspect()) { if (aspectRatio * width > height) { width = 1 / (aspectRatio / height); } else if (1 / (aspectRatio / height) > width) { height = aspectRatio * width; } } if (width > 0 && height > 0) { // Autocenter the control //getSkinnable().setTranslateX((getSkinnable().getWidth() - width) * 0.5); //getSkinnable().setTranslateY((getSkinnable().getHeight() - height) * 0.5); flapHeight = 0.49206349206349204 * height; fixtureRight.setPrefSize(0.0476190476 * height, 0.164021164021164 * height); fixtureRight.setTranslateX(width - 0.0476190476 * height); fixtureRight.setTranslateY(0.41798941798941797 * height); fixtureLeft.setPrefSize(0.0476190476 * height, 0.164021164021164 * height); fixtureLeft.setTranslateY(0.41798941798941797 * height); if (width > height && width > 0 && height > 0) { final Path UPPER = new Path(); UPPER.setFillRule(FillRule.EVEN_ODD); UPPER.getElements().add(new MoveTo(width, 0.0)); UPPER.getElements().add(new LineTo(width, 0.4074074074074074 * height)); UPPER.getElements().add(new LineTo(width - 0.0582010582 * height, 0.4074074074074074 * height)); UPPER.getElements().add(new LineTo(width - 0.0582010582 * height, 0.49206349206349204 * height)); UPPER.getElements().add(new LineTo(0.0582010582 * height, 0.49206349206349204 * height)); UPPER.getElements().add(new LineTo(0.0582010582 * height, 0.4074074074074074 * height)); UPPER.getElements().add(new LineTo(0.0, 0.4074074074074074 * height)); UPPER.getElements().add(new LineTo(0.0, 0.0)); UPPER.getElements().add(new LineTo(width, 0.0)); UPPER.getElements().add(new ClosePath()); final String UPPER_SVG = ShapeConverter.shapeToSvgString(UPPER); final Path LOWER = new Path(); LOWER.setFillRule(FillRule.EVEN_ODD); LOWER.getElements().add(new MoveTo(width, height)); LOWER.getElements().add(new LineTo(width, 0.5925925925925926 * height)); LOWER.getElements().add(new LineTo(width - 0.0582010582 * height, 0.5925925925925926 * height)); LOWER.getElements().add(new LineTo(width - 0.0582010582 * height, 0.5079365079365079 * height)); LOWER.getElements().add(new LineTo(0.0582010582 * height, 0.5079365079365079 * height)); LOWER.getElements().add(new LineTo(0.0582010582 * height, 0.5925925925925926 * height)); LOWER.getElements().add(new LineTo(0.0, 0.5925925925925926 * height)); LOWER.getElements().add(new LineTo(0.0, height)); LOWER.getElements().add(new LineTo(width, height)); LOWER.getElements().add(new ClosePath()); final String LOWER_SVG = ShapeConverter.shapeToSvgString(LOWER); upperBackground.setStyle(new StringBuilder("-fx-shape:").append("\"").append(UPPER_SVG).append("\";").toString()); lowerBackground.setStyle(new StringBuilder("-fx-shape:").append("\"").append(LOWER_SVG).append("\";").toString()); flap.setStyle(new StringBuilder("-fx-shape:").append("\"").append(UPPER_SVG).append("\";").toString()); } upperBackground.setPrefSize(width, flapHeight); lowerBackground.setPrefSize(width, flapHeight); lowerBackground.setTranslateY(0.5079365079365079 * height); //font = Font.font("Bebas Neue", height * 0.9); font = Font.font("Droid Sans Mono", height * 0.75); upperBackgroundText.setWidth(width); upperBackgroundText.setHeight(flapHeight); lowerBackgroundText.setWidth(width); lowerBackgroundText.setHeight(flapHeight); lowerBackgroundText.setTranslateY(0.5079365079365079 * height); flap.setPrefSize(width, flapHeight); rotateFlap.setPivotY(height * 0.5); flapTextFront.setWidth(width); flapTextFront.setHeight(flapHeight); flapTextBack.setWidth(width); flapTextBack.setHeight(flapHeight); ctxUpperBackgroundText.setFont(font); ctxLowerBackgroundText.setFont(font); ctxTextFront.setFont(font); ctxTextBack.setFont(font); refreshTextCtx(); innerShadow.setOffsetY(-0.01 * flapHeight); innerShadow.setRadius(0.01 * flapHeight); innerHighlight.setOffsetY(0.01 * flapHeight); innerHighlight.setRadius(0.01 * flapHeight); reversedInnerShadow.setOffsetY(-0.01 * 0.4920634921 * height); reversedInnerShadow.setRadius(0.01 * 0.4920634921 * height); reversedInnerHighlight.setOffsetY(0.01 * 0.4920634921 * height); reversedInnerHighlight.setRadius(0.01 * 0.4920634921 * height); } } }