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

jfxtras.labs.internal.scene.control.skin.SlideLockSkin Maven / Gradle / Ivy

There is a newer version: 9.0-r1
Show newest version
/**
 * SlideLockSkin.java
 *
 * Copyright (c) 2011-2014, JFXtras
 * 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 organization 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  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 jfxtras.labs.internal.scene.control.skin;


import static jfxtras.labs.scene.control.SlideLock.PREFERRED_HEIGHT;
import static jfxtras.labs.scene.control.SlideLock.PREFERRED_WIDTH;
import javafx.animation.AnimationTimer;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.Skin;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TouchEvent;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradientBuilder;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.RadialGradientBuilder;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.RectangleBuilder;
import javafx.scene.shape.SVGPath;
import javafx.scene.shape.SVGPathBuilder;
import javafx.scene.text.Text;
import javafx.scene.transform.Scale;
import javafx.scene.transform.ScaleBuilder;
import jfxtras.labs.scene.control.SlideLock;

/**
 * This represents the actual drawing of the slide to unlock control. All mouse and touch event handlers are
 * managed in this class.
 *
 * @author cdea
 */
public class SlideLockSkin extends Region implements Skin{
    private final SlideLock          CONTROL;
    private Group                    button;
    private Text                     text;
    private EventHandler mouseHandler;
    private EventHandler touchHandler;
    private AnimationTimer           currentSpotlightAnim;
    
    public SlideLockSkin(final SlideLock slideLock) {
        this.CONTROL = slideLock;

        if (CONTROL.getPrefWidth() <= 0 || CONTROL.getPrefHeight() <= 0) {
            CONTROL.setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        }

        button = new Group();
        text   = new Text(CONTROL.getText());

        mouseHandler = new EventHandler() {
            @Override public void handle(final MouseEvent event) {
                if (MouseEvent.MOUSE_PRESSED == event.getEventType()) {
                    buttonPressAction(button, event.getX(), event.getY());
                } else if (MouseEvent.MOUSE_DRAGGED == event.getEventType()) {
                    moveButtonAction(button, event.getX(), event.getY());
                } else if (MouseEvent.MOUSE_RELEASED == event.getEventType()) {
                    buttonSnapBack();
                }
            }
        };

        touchHandler = new EventHandler() {
            @Override public void handle(final TouchEvent event) {
                if (TouchEvent.TOUCH_PRESSED == event.getEventType()) {
                    buttonPressAction(button, event.getTouchPoint().getX(), event.getTouchPoint().getY());
                } else if (TouchEvent.TOUCH_MOVED == event.getEventType()) {
                    moveButtonAction(button, event.getTouchPoint().getX(), event.getTouchPoint().getY());
                } else if (TouchEvent.TOUCH_RELEASED == event.getEventType()) {
                    buttonSnapBack();
                }
            }
        };

        setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);

        drawControl();
        addHandlers();
        startAnimations();
    }

    /**
     * This method builds an AnimationTimer and starts it to provide periodic animations.
     * If the skin is to call the layoutChildren() method it is important to stop and recreate if scaling changes.
     * Since this is mainly to animate the panning of a spotlight on the text node the startAnimation() method should
     * be called after the text node on the Scene because the animation needs to know how wide the text is in order
     * to pan across. (presentation logic)
     */
    private void startAnimations() {

        // create a spot light animation across the letters.
        final double spotlightRadius = 70;
        if (currentSpotlightAnim != null) {
            currentSpotlightAnim.stop();
        }
        currentSpotlightAnim = new AnimationTimer() {
            long startTime = System.currentTimeMillis();
            long duration = 0;
            double x = -spotlightRadius;
            @Override
            public void handle(long now) {

                long elapsedTime = System.currentTimeMillis() - startTime;
                duration=duration+elapsedTime;
                if (duration > 150) {
                    RadialGradient lightColor = RadialGradientBuilder.create()
                            .radius(spotlightRadius)
                            .centerX(x)
                            .centerY(text.getY())
                            .proportional(false)
                            .stops(new Stop(0, Color.WHITE),
                                    new Stop(.5, Color.WHITE),
                                    new Stop(1, Color.web("#555555")))
                            .build();
                    x=x+10;
                    if (x > text.getBoundsInParent().getMaxX() + spotlightRadius) {
                        x=-spotlightRadius;
                    }
                    text.setFill(lightColor);
                    startTime = System.currentTimeMillis();
                    duration = 0;
                }
            }
        };
        currentSpotlightAnim.start();
    }

    @Override public void layoutChildren() {
        drawControl();
        startAnimations();
        super.layoutChildren();
    }

    @Override public SlideLock getSkinnable() {
        return CONTROL;
    }

    @Override public Node getNode() {
        return this;
    }

    @Override public void dispose() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private void addHandlers() {
        // MouseEvents
        setOnMousePressed(mouseHandler);
        setOnMouseDragged(mouseHandler);
        setOnMouseReleased(mouseHandler);
        // TouchEvents
        setOnTouchPressed(touchHandler);
        setOnTouchMoved(touchHandler);
        setOnTouchReleased(touchHandler);
    }

    private void buttonPressAction(final Node slideButton, final double X, final double Y) {
        if (slideButton.getBoundsInParent().contains(X, Y)) {
            CONTROL.setStartX(X - CONTROL.getEndX());
        }
    }

    private void moveButtonAction(final Node slideButton, final double X, final double Y) {
        final double WIDTH          = CONTROL.getPrefWidth();
        final double HEIGHT         = CONTROL.getPrefHeight();
        final double SCALE_FACTOR_X = WIDTH / PREFERRED_WIDTH;
        final double SCALE_FACTOR_Y = HEIGHT / PREFERRED_HEIGHT;

        System.out.println(X + ", " + Y);

        // check when user pressed the button
        if (slideButton.getBoundsInParent().contains(X, Y)) {
            CONTROL.setEndX(X - CONTROL.getStartX());
            if (slideButton.getTranslateX() < SlideLock.START_XCOORD) {
                CONTROL.setLocked(true);
                CONTROL.setEndX(SlideLock.START_XCOORD);
            } else if (slideButton.getTranslateX() > SlideLock.END_XCOORD) {
                CONTROL.setLocked(false);
                CONTROL.setEndX(SlideLock.END_XCOORD);
            } else {
                CONTROL.setLocked(true);
            }
            double opacity = 1d - (slideButton.getTranslateX()) / 200.0;
            if (opacity < 0) {
                opacity = 0;
            }
            CONTROL.setTextOpacity(opacity);
            currentSpotlightAnim.stop();
        }
    }


    // ******************** Drawing related ***********************************
    private void buttonSnapBack() {
        if (CONTROL.isLocked()){
            CONTROL.getSnapButtonBackAnim().play();
            currentSpotlightAnim.start();
        }
    }

    private void drawControl() {
        final double WIDTH          = CONTROL.getPrefWidth();
        final double HEIGHT         = CONTROL.getPrefHeight();
        final double SCALE_FACTOR_X = WIDTH / PREFERRED_WIDTH;
        final double SCALE_FACTOR_Y = HEIGHT / PREFERRED_HEIGHT;
        final Scale SCALE           = ScaleBuilder.create()
                .x(WIDTH / PREFERRED_WIDTH)
                .y(HEIGHT / PREFERRED_HEIGHT)
                .pivotX(0)
                .pivotY(0)
                .build();

        Rectangle backgroundRect = RectangleBuilder.create()
                .id("slide-background")
                .width(WIDTH)
                .height(HEIGHT)
                .build();
        backgroundRect.visibleProperty().bind(CONTROL.backgroundVisibleProperty());

        Rectangle slideArea = RectangleBuilder.create()
                .id("slide-area")
                .x(0.0612476117 * WIDTH)
                .y(0.2463297872 * HEIGHT)
                .width(0.8829200973 * WIDTH)
                .height(0.5319148936 * HEIGHT)
                .arcWidth(0.079787234 * HEIGHT)
                .arcHeight(0.079787234 * HEIGHT)
                .build();

        SVGPath glareRect = SVGPathBuilder.create()
                .fill(LinearGradientBuilder.create()
                        .proportional(true)
                        .startX(0)
                        .startY(0)
                        .endX(0)
                        .endY(1)
                        .stops(new Stop(0, Color.web("f0f0f0", 1)),
                                new Stop(1, Color.web("f0f0f0", 0))
                        )
                        .build()
                )
                .opacity(.274)
                .transforms(SCALE)
                .content("m 0,0 0,94 32,0 0,-27.218747 C 30.998808,55.222973 37.761737,45.9354 46.156457,45.93665 l 431.687503,0.06427 c 8.39472,0.0013 15.15487,9.290837 15.15315,20.814756 l -0.004,27.218754 30.28125,0 0,-94.0000031 L 0,0 z")
                .id("glare-frame")
                .build();
        glareRect.visibleProperty().bind(CONTROL.backgroundVisibleProperty());

        text.setText(CONTROL.getText());
        text.setId("slide-text");
        text.getTransforms().clear();
        text.getTransforms().add(SCALE);

        drawSlideButton();
        button.translateXProperty().bind(CONTROL.endXProperty().multiply(SCALE_FACTOR_X));
        button.setTranslateY(SlideLock.BUTTON_YCOORD * SCALE_FACTOR_Y);

        text.setTranslateX(SlideLock.START_XCOORD + button.getBoundsInParent().getWidth() + 0.1063829787 * HEIGHT);
        text.setTranslateY(0.5744680851 * HEIGHT);
        text.opacityProperty().bind(CONTROL.textOpacityProperty());

        Rectangle topGlareRect = RectangleBuilder.create()
                .id("slide-top-glare")
                .fill(Color.WHITE)
                .width(WIDTH)
                .height(0.5 * HEIGHT)
                .opacity(0.0627451)
                .build();
        topGlareRect.visibleProperty().bind(CONTROL.backgroundVisibleProperty());
        getChildren().clear();
        getChildren().addAll(backgroundRect, slideArea, glareRect, text, button, topGlareRect);
    }

    private void drawSlideButton() {
        final double WIDTH   = CONTROL.getPrefWidth();
        final double HEIGHT  = CONTROL.getPrefHeight();
        final double SCALE_X = WIDTH / PREFERRED_WIDTH;
        final double SCALE_Y = HEIGHT / PREFERRED_HEIGHT;
        Scale scale = new Scale();
        scale.setX(SCALE_X);
        scale.setY(SCALE_Y);
        scale.setPivotX(0);
        scale.setPivotY(0);

        button.getChildren().clear();

        // build gradientRect
        Rectangle gradientRect = RectangleBuilder.create()
                .x(0.0358943492 * WIDTH)
                .y(0.0649521277 * HEIGHT)
                .width(0.156464544 * WIDTH)
                .height(0.3616446809 * HEIGHT)
                .arcWidth(0.0929042553 * HEIGHT)
                .arcHeight(0.0929042553 * HEIGHT)
                        //.fill(Color.WHITE)
                .fill(CONTROL.getButtonArrowBackgroundColor())
                .id("button-gradient-rect")
                .build();
        button.getChildren().add(gradientRect);

        // build arrowBlurShadow
        SVGPath arrowBlurShadow = SVGPathBuilder.create()
                .fill(Color.BLACK)
                .effect(new GaussianBlur(5))
                .transforms(scale)
                .content("m 17.40912,2.47162 c -8.27303,0 -14.9375,7.04253 -14.9375,15.78125 l 0,59.9375 c 0,8.73872 6.66447,15.75 14.9375,15.75 l 84.625,0 c 8.27303,0 14.9375,-7.01128 14.9375,-15.75 l 0,-59.9375 c 0,-8.73872 -6.66447,-15.78125 -14.9375,-15.78125 l -84.625,0 z m 45.0625,18.15625 27.5625,27.59375 -27.5625,27.5625 0,-15.5625 -33.0625,0 0,-24 33.0625,0 0,-15.59375 z")
                .id("#button-arrow-blur-shadow")
                .build();
        button.getChildren().add(arrowBlurShadow);

        // build arrowStencilCrisp
        SVGPath arrowStencilCrisp = SVGPathBuilder.create()
                .content("m 17.40912,0.47162 c -8.27303,0 -14.9375,7.04253 -14.9375,15.78125 l 0,59.9375 c 0,8.73872 6.66447,15.75 14.9375,15.75 l 84.625,0 c 8.27303,0 14.9375,-7.01128 14.9375,-15.75 l 0,-59.9375 c 0,-8.73872 -6.66447,-15.78125 -14.9375,-15.78125 l -84.625,0 z m 45.0625,18.15625 27.5625,27.59375 -27.5625,27.5625 0,-15.5625 -33.0625,0 0,-24 33.0625,0 0,-15.59375 z")
                .fill(CONTROL.getButtonColor())
                .id("#button-arrow-stencil-crisp")
                .transforms(scale)
                .build();
        button.getChildren().add(arrowStencilCrisp);

        // build glareRect
        SVGPath glareRect = SVGPathBuilder.create()
                .content("m 17.83252,1.67757 c -8.27303,0 -14.9375,7.21042 -14.9375,16.15746 l 0,28.31557 114.5,0 0,-28.31557 c 0,-8.94704 -6.66447,-16.15746 -14.9375,-16.15746 l -84.625,0 z")
                .fill(LinearGradientBuilder.create()
                        .proportional(true)
                        .startX(0)
                        .startY(1)
                        .endX(0)
                        .endY(0)
                        .stops(new Stop(0, Color.web("f4f4f4", 0.60)),
                                new Stop(1, Color.web("ffffff", 0.2063063)))
                        .build()
                )
                .id("#button-arrow-glare-rect")
                .transforms(scale)
                .build();
        glareRect.visibleProperty().bind(CONTROL.buttonGlareVisibleProperty()); // red button
        button.getChildren().add(glareRect);
        button.setCache(true);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy