All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jfoenix.skins.JFXSpinnerSkin Maven / Gradle / Ivy

There is a newer version: 9.0.10
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.jfoenix.skins;

import com.jfoenix.controls.JFXSpinner;
import com.sun.javafx.scene.NodeHelper;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.Group;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.util.Duration;

/**
 * JFXSpinner material design skin
 *
 * @author Shadi Shaheen & Gerard Moubarak
 * @version 1.0
 * @since 2017-09-25
 */
public class JFXSpinnerSkin extends SkinBase {

    private JFXSpinner control;
    private boolean isValid = false;

    private Color greenColor;
    private Color redColor;
    private Color yellowColor;
    private Color blueColor;
    private Timeline timeline;
    private Arc arc;
    private final StackPane arcPane;
    private final Rectangle fillRect;
    private double arcLength = -1;
    private Text text;

    public JFXSpinnerSkin(JFXSpinner control) {
        super(control);

        this.control = control;

        blueColor = Color.valueOf("#4285f4");
        redColor = Color.valueOf("#db4437");
        yellowColor = Color.valueOf("#f4b400");
        greenColor = Color.valueOf("#0F9D58");

        arc = new Arc();
        arc.setManaged(false);
        arc.setStartAngle(0);
        arc.setLength(180);
        arc.getStyleClass().setAll("arc");
        arc.setFill(Color.TRANSPARENT);
        arc.setStrokeWidth(3);

        fillRect = new Rectangle();
        fillRect.setFill(Color.TRANSPARENT);
        text = new Text();
        text.getStyleClass().setAll("text", "percentage");
        final Group group = new Group(fillRect, arc, text);
        group.setManaged(false);
        arcPane = new StackPane(group);
        arcPane.setPrefSize(50, 50);
        getChildren().setAll(arcPane);

        // register listeners
        registerChangeListener(control.indeterminateProperty(), obs -> initialize());
        registerChangeListener(control.progressProperty(), obs -> updateProgress());
        registerChangeListener(control.visibleProperty(), obs->updateAnimation());
        registerChangeListener(control.parentProperty(), obs->updateAnimation());
        registerChangeListener(control.sceneProperty(), obs->updateAnimation());
    }

    private void initialize() {
        if (getSkinnable().isIndeterminate()) {
            if (timeline == null) {
                createTransition();
                if (NodeHelper.isTreeShowing(getSkinnable())) {
                    timeline.play();
                }
            }
        } else {
            clearAnimation();
            arc.setStartAngle(90);
            updateProgress();
        }
    }

    private KeyFrame[] getKeyFrames(double angle, double duration, Paint color) {
        KeyFrame[] frames = new KeyFrame[4];
        frames[0] = new KeyFrame(Duration.seconds(duration),
            new KeyValue(arc.lengthProperty(), 5, Interpolator.LINEAR),
            new KeyValue(arc.startAngleProperty(),
                angle + 45 + control.getStartingAngle(),
                Interpolator.LINEAR));
        frames[1] = new KeyFrame(Duration.seconds(duration + 0.4),
            new KeyValue(arc.lengthProperty(), 250, Interpolator.LINEAR),
            new KeyValue(arc.startAngleProperty(),
                angle + 90 + control.getStartingAngle(),
                Interpolator.LINEAR));
        frames[2] = new KeyFrame(Duration.seconds(duration + 0.7),
            new KeyValue(arc.lengthProperty(), 250, Interpolator.LINEAR),
            new KeyValue(arc.startAngleProperty(),
                angle + 135 + control.getStartingAngle(),
                Interpolator.LINEAR));
        frames[3] = new KeyFrame(Duration.seconds(duration + 1.1),
            new KeyValue(arc.lengthProperty(), 5, Interpolator.LINEAR),
            new KeyValue(arc.startAngleProperty(),
                angle + 435 + control.getStartingAngle(),
                Interpolator.LINEAR),
            new KeyValue(arc.strokeProperty(), color, Interpolator.EASE_BOTH));
        return frames;
    }

    private void pauseTimeline(boolean pause) {
        if (getSkinnable().isIndeterminate()) {
            if (timeline == null) {
                createTransition();
            }
            if (pause) {
                timeline.pause();
            } else {
                timeline.play();
            }
        }
    }

    private void updateAnimation() {
        ProgressIndicator control = getSkinnable();
        final boolean isTreeVisible = control.isVisible() &&
                                      control.getParent() != null &&
                                      control.getScene() != null;
        if (timeline != null) {
            pauseTimeline(!isTreeVisible);
        } else if (isTreeVisible) {
            createTransition();
        }
    }

    @Override
    protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
        if (Region.USE_COMPUTED_SIZE == control.getRadius()) {
            return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset);
        } else {
            return control.getRadius() * 2 + arc.getStrokeWidth() * 2;
        }
    }

    @Override
    protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
        if (Region.USE_COMPUTED_SIZE == control.getRadius()) {
            return super.computeMaxHeight(height, topInset, rightInset, bottomInset, leftInset);
        } else {
            return control.getRadius() * 2 + arc.getStrokeWidth() * 2;
        }
    }

    @Override
    protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
        return arcPane.prefWidth(-1);
    }

    @Override
    protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
        return arcPane.prefHeight(-1);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
        final double strokeWidth = arc.getStrokeWidth();
        final double radius = Math.min(contentWidth, contentHeight) / 2 - strokeWidth / 2;
        final double arcSize = snapSize(radius * 2 + strokeWidth);

        arcPane.resizeRelocate((contentWidth - arcSize) / 2 + 1, (contentHeight - arcSize) / 2 + 1, arcSize, arcSize);
        arc.setRadiusX(radius);
        arc.setRadiusY(radius);
        arc.setCenterX(arcSize / 2);
        arc.setCenterY(arcSize / 2);

        fillRect.setWidth(arcSize);
        fillRect.setHeight(arcSize);

        if (!isValid) {
            initialize();
            isValid = true;
        }

        if (!getSkinnable().isIndeterminate()) {
            arc.setLength(arcLength);
            if (text.isVisible()) {
                final double progress = control.getProgress();
                int intProgress = (int) Math.round(progress * 100.0);
                Font font = text.getFont();
                text.setFont(Font.font(font.getFamily(), radius / 1.7));
                text.setText((progress > 1 ? 100 : intProgress) + "%");
                text.relocate((arcSize - text.getLayoutBounds().getWidth()) / 2, (arcSize - text.getLayoutBounds().getHeight()) / 2);
            }
        }
    }

    boolean wasIndeterminate = false;

    protected void updateProgress() {
        final ProgressIndicator control = getSkinnable();
        final boolean isIndeterminate = control.isIndeterminate();
        if (!(isIndeterminate && wasIndeterminate)) {
            arcLength = -360 * control.getProgress();
            control.requestLayout();
        }
        wasIndeterminate = isIndeterminate;
    }

    private void createTransition() {
        if(!getSkinnable().isIndeterminate()) return;
        final Paint initialColor = arc.getStroke();
        if (initialColor == null) {
            arc.setStroke(blueColor);
        }

        KeyFrame[] blueFrame = getKeyFrames(0, 0, initialColor == null ? blueColor : initialColor);
        KeyFrame[] redFrame = getKeyFrames(450, 1.4, initialColor == null ? redColor : initialColor);
        KeyFrame[] yellowFrame = getKeyFrames(900, 2.8, initialColor == null ? yellowColor : initialColor);
        KeyFrame[] greenFrame = getKeyFrames(1350, 4.2, initialColor == null ? greenColor : initialColor);

        KeyFrame endingFrame = new KeyFrame(Duration.seconds(5.6),
            new KeyValue(arc.lengthProperty(), 5, Interpolator.LINEAR),
            new KeyValue(arc.startAngleProperty(),
                1845 + control.getStartingAngle(),
                Interpolator.LINEAR));

        if (timeline != null) {
            timeline.stop();
            timeline.getKeyFrames().clear();
        }
        timeline = new Timeline(blueFrame[0],
            blueFrame[1],
            blueFrame[2],
            blueFrame[3],
            redFrame[0],
            redFrame[1],
            redFrame[2],
            redFrame[3],
            yellowFrame[0],
            yellowFrame[1],
            yellowFrame[2],
            yellowFrame[3],
            greenFrame[0],
            greenFrame[1],
            greenFrame[2],
            greenFrame[3],
            endingFrame);
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.setDelay(Duration.ZERO);
        timeline.playFromStart();
    }

    private void clearAnimation() {
        if (timeline != null) {
            timeline.stop();
            timeline.getKeyFrames().clear();
            timeline = null;
        }
    }

    @Override
    public void dispose() {
        super.dispose();
        clearAnimation();
        arc = null;
        control = null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy