/* * 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 jfx8controls.extendingcontrol; import com.sun.javafx.scene.control.skin.LabeledSkinBase; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.geometry.HPos; import javafx.geometry.NodeOrientation; import javafx.geometry.VPos; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import javafx.util.Duration; /** * User: hansolo * Date: 07.10.13 * Time: 07:45 */ public class SlideCheckBoxSkin extends LabeledSkinBase<SlideCheckBox, SlideCheckBoxBehavior<SlideCheckBox>> { private static final double BOX_WIDTH = 104; private static final double BOX_HEIGHT = 42; private static final double THUMB_WIDTH = 48; private static final double THUMB_HEIGHT = 34; private final StackPane box = new StackPane(); private Region markBox; private Region crossBox; private Region thumb; private Timeline selectTimeline; private Timeline deselectTimeline; // ******************** Constructors ************************************** public SlideCheckBoxSkin(SlideCheckBox checkbox) { super(checkbox, new SlideCheckBoxBehavior<>(checkbox)); initGraphics(); initTimelines(); registerListeners(); } // ******************** Initialization ************************************ private void initGraphics() { markBox = new Region(); markBox.getStyleClass().setAll("mark"); markBox.setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); markBox.setTranslateX(-27); crossBox = new Region(); crossBox.getStyleClass().setAll("cross"); crossBox.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); crossBox.setTranslateX(27); thumb = new Region(); thumb.getStyleClass().setAll("thumb"); thumb.setMinSize(48, 34); thumb.setPrefSize(48, 34); thumb.setMaxSize(48, 34); if (getSkinnable().isSelected()) { crossBox.setOpacity(0); thumb.setTranslateX(24); } else { markBox.setOpacity(0); thumb.setTranslateX(-24); } box.getStyleClass().setAll("box"); box.getChildren().addAll(markBox, crossBox, thumb); updateChildren(); } private void initTimelines() { selectTimeline = getSelectTimeline(); deselectTimeline = getDeselectTimeline(); } private void registerListeners() { getSkinnable().selectedProperty().addListener(observable -> toggle()); } // ******************** Methods ******************************************* @Override protected void updateChildren() { super.updateChildren(); if (box != null) { getChildren().add(box); } } @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset) + snapSize(box.minWidth(-1)); } @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { return Math.max(super.computeMinHeight(width - box.minWidth(-1), topInset, rightInset, bottomInset, leftInset), topInset + box.minHeight(-1) + bottomInset); } @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) { return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset) + snapSize(box.prefWidth(-1) + 46); } @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) { return Math.max(super.computePrefHeight(width - box.prefWidth(-1), topInset, rightInset, bottomInset, leftInset), topInset + box.prefHeight(-1) + bottomInset); } @Override protected void layoutChildren(final double x, final double y, final double w, final double h) { final SlideCheckBox checkBox = getSkinnable(); final double computeWidth = Math.max(checkBox.prefWidth(-1), checkBox.minWidth(-1)); final double labelWidth = Math.min( computeWidth - BOX_WIDTH, w - snapSize(BOX_WIDTH) + 100); final double labelHeight = Math.min(checkBox.prefHeight(labelWidth), h); final double maxHeight = Math.max(BOX_HEIGHT, labelHeight); final double xOffset = computeXOffset(w, labelWidth + BOX_WIDTH, checkBox.getAlignment().getHpos()) + x; final double yOffset = computeYOffset(h, maxHeight, checkBox.getAlignment().getVpos()) + x; layoutLabelInArea(xOffset + BOX_WIDTH, yOffset, labelWidth, maxHeight, checkBox.getAlignment()); thumb.resize(THUMB_WIDTH, THUMB_HEIGHT); box.resize(BOX_WIDTH, BOX_HEIGHT); positionInArea(box, xOffset, yOffset, BOX_WIDTH, maxHeight, 0, checkBox.getAlignment().getHpos(), checkBox.getAlignment().getVpos()); } private void toggle() { if (getSkinnable().isSelected()) { selectTimeline.play(); } else { deselectTimeline.play(); } } private Timeline getSelectTimeline() { final KeyValue kvThumbStartTranslateSelected = new KeyValue(thumb.translateXProperty(), -24, Interpolator.EASE_BOTH); final KeyValue kvThumbEndTranslateSelected = new KeyValue(thumb.translateXProperty(), 24, Interpolator.EASE_BOTH); final KeyValue kvMarkStartOpacitySelected = new KeyValue(markBox.opacityProperty(), 0, Interpolator.EASE_BOTH); final KeyValue kvMarkEndOpacitySelected = new KeyValue(markBox.opacityProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvMarkStartScaleXSelected = new KeyValue(markBox.scaleXProperty(), 0, Interpolator.EASE_BOTH); final KeyValue kvMarkEndScaleXSelected = new KeyValue(markBox.scaleXProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvMarkStartScaleYSelected = new KeyValue(markBox.scaleYProperty(), 0, Interpolator.EASE_BOTH); final KeyValue kvMarkEndScaleYSelected = new KeyValue(markBox.scaleYProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvMarkStartScaleUpXSelected = new KeyValue(markBox.scaleXProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvMarkEndScaleUpXSelected = new KeyValue(markBox.scaleXProperty(), 1.5, Interpolator.EASE_BOTH); final KeyValue kvMarkStartScaleUpYSelected = new KeyValue(markBox.scaleYProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvMarkEndScaleUpYSelected = new KeyValue(markBox.scaleYProperty(), 1.5, Interpolator.EASE_BOTH); final KeyValue kvMarkStartScaleDownXSelected = new KeyValue(markBox.scaleXProperty(), 1.5, Interpolator.EASE_BOTH); final KeyValue kvMarkEndScaleDownXSelected = new KeyValue(markBox.scaleXProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvMarkStartScaleDownYSelected = new KeyValue(markBox.scaleYProperty(), 1.5, Interpolator.EASE_BOTH); final KeyValue kvMarkEndScaleDownYSelected = new KeyValue(markBox.scaleYProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvCrossStartOpacitySelected = new KeyValue(crossBox.opacityProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvCrossEndOpacitySelected = new KeyValue(crossBox.opacityProperty(), 0, Interpolator.EASE_BOTH); final KeyValue kvCrossStartScaleXSelected = new KeyValue(crossBox.scaleXProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvCrossEndScaleXSelected = new KeyValue(crossBox.scaleXProperty(), 0, Interpolator.EASE_BOTH); final KeyValue kvCrossStartScaleYSelected = new KeyValue(crossBox.scaleYProperty(), 1, Interpolator.EASE_BOTH); final KeyValue kvCrossEndScaleYSelected = new KeyValue(crossBox.scaleYProperty(), 0, Interpolator.EASE_BOTH); final KeyFrame kfStart = new KeyFrame(Duration.ZERO, kvThumbStartTranslateSelected, kvMarkStartOpacitySelected, kvMarkStartScaleXSelected, kvMarkStartScaleYSelected, kvCrossStartOpacitySelected, kvCrossStartScaleXSelected, kvCrossStartScaleYSelected); final KeyFrame kfEnd = new KeyFrame(Duration.millis(180), kvThumbEndTranslateSelected, kvMarkEndOpacitySelected, kvMarkEndScaleXSelected, kvMarkEndScaleYSelected, kvCrossEndOpacitySelected, kvCrossEndScaleXSelected, kvCrossEndScaleYSelected); final KeyFrame kfScaleUpStart = new KeyFrame(Duration.millis(250), kvMarkStartScaleUpXSelected, kvMarkStartScaleUpYSelected); final KeyFrame kfScaleUpEnd = new KeyFrame(Duration.millis(350), kvMarkEndScaleUpXSelected, kvMarkEndScaleUpYSelected); final KeyFrame kfScaleDownStart = new KeyFrame(Duration.millis(350), kvMarkStartScaleDownXSelected, kvMarkStartScaleDownYSelected); final KeyFrame kfScaleDownEnd = new KeyFrame(Duration.millis(500), kvMarkEndScaleDownXSelected, kvMarkEndScaleDownYSelected); final Timeline timeline = new Timeline(); timeline.getKeyFrames().setAll(kfStart, kfEnd, kfScaleUpStart, kfScaleUpEnd, kfScaleDownStart, kfScaleDownEnd); return timeline; } private Timeline getDeselectTimeline() { final KeyValue kvThumbStartTranslateDeselect = new KeyValue(thumb.translateXProperty(), 24); final KeyValue kvThumbEndTranslateDeselect = new KeyValue(thumb.translateXProperty(), -24); final KeyValue kvMarkStartOpacityDeselect = new KeyValue(markBox.opacityProperty(), 1); final KeyValue kvMarkEndOpacityDeselect = new KeyValue(markBox.opacityProperty(), 0); final KeyValue kvMarkStartScaleXDeselect = new KeyValue(markBox.scaleXProperty(), 1); final KeyValue kvMarkEndScaleXDeselect = new KeyValue(markBox.scaleXProperty(), 0); final KeyValue kvMarkStartScaleYDeselect = new KeyValue(markBox.scaleYProperty(), 1); final KeyValue kvMarkEndScaleYDeselect = new KeyValue(markBox.scaleYProperty(), 0); final KeyValue kvCrossStartOpacityDeselect = new KeyValue(crossBox.opacityProperty(), 0); final KeyValue kvCrossEndOpacityDeselect = new KeyValue(crossBox.opacityProperty(), 1); final KeyValue kvCrossStartScaleXDeselect = new KeyValue(crossBox.scaleXProperty(), 0); final KeyValue kvCrossEndScaleXDeselect = new KeyValue(crossBox.scaleXProperty(), 1); final KeyValue kvCrossStartScaleYDeselect = new KeyValue(crossBox.scaleYProperty(), 0); final KeyValue kvCrossEndScaleYDeselect = new KeyValue(crossBox.scaleYProperty(), 1); final KeyValue kvCrossRotateStart = new KeyValue(crossBox.rotateProperty(), 0); final KeyValue kvCrossRotateEnd = new KeyValue(crossBox.rotateProperty(), 360); final KeyFrame kfStart = new KeyFrame(Duration.ZERO, kvThumbStartTranslateDeselect, kvMarkStartOpacityDeselect, kvMarkStartScaleXDeselect, kvMarkStartScaleYDeselect, kvCrossStartOpacityDeselect, kvCrossStartScaleXDeselect, kvCrossStartScaleYDeselect); final KeyFrame kfEnd = new KeyFrame(Duration.millis(180), kvThumbEndTranslateDeselect, kvMarkEndOpacityDeselect, kvMarkEndScaleXDeselect, kvMarkEndScaleYDeselect, kvCrossEndOpacityDeselect, kvCrossEndScaleXDeselect, kvCrossEndScaleYDeselect); final KeyFrame kfRotateStart = new KeyFrame(Duration.millis(250), kvCrossRotateStart); final KeyFrame kfRotateEnd = new KeyFrame(Duration.millis(750), kvCrossRotateEnd); final Timeline timeline = new Timeline(); timeline.getKeyFrames().setAll(kfStart, kfEnd, kfRotateStart, kfRotateEnd); timeline.setOnFinished(actionEvent -> crossBox.setRotate(0)); return timeline; } private static double computeXOffset(double width, double contentWidth, HPos hpos) { switch(hpos) { case LEFT: return 0; case CENTER: return (width - contentWidth) / 2; case RIGHT: return width - contentWidth; } return 0; } private static double computeYOffset(double height, double contentHeight, VPos vpos) { switch(vpos) { case TOP: return 0; case CENTER: return (height - contentHeight) / 2; case BOTTOM: return height - contentHeight; default: return 0; } } }