Package eu.hansolo.enzo.roundlcdclock

Source Code of eu.hansolo.enzo.roundlcdclock.RoundLcdClock

/*
* 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.roundlcdclock;/**
* Created by
* User: hansolo
* Date: 11.07.13
* Time: 06:17
*/

import javafx.animation.AnimationTimer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.VPos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;


public class RoundLcdClock extends Region {
    private static final double       PREFERRED_WIDTH  = 200;
    private static final double       PREFERRED_HEIGHT = 200;
    private static final double       MINIMUM_WIDTH    = 25;
    private static final double       MINIMUM_HEIGHT   = 25;
    private static final double       MAXIMUM_WIDTH    = 1024;
    private static final double       MAXIMUM_HEIGHT   = 1024;
    private double                    size;
    private double                    width;
    private double                    height;
    private ObjectProperty<Color>     hColor;
    private ObjectProperty<Color>     mColor;
    private ObjectProperty<Color>     m5Color;
    private ObjectProperty<Color>     sColor;
    private ObjectProperty<Color>     timeColor;
    private ObjectProperty<Color>     dateColor;
    private BooleanProperty           alarmOn;
    private ObjectProperty<LocalTime> alarm;
    private BooleanProperty           dateVisible;
    private BooleanProperty           alarmVisible;
    private Pane                      pane;
    private Canvas                    canvasBkg;
    private GraphicsContext           ctxBkg;
    private Canvas                    canvasFg;
    private GraphicsContext           ctxFg;
    private Font                      font;
    private int                       hours;
    private int                       minutes;
    private int                       seconds;
    private boolean                   pm;
    private StringBuilder             time;
    private long                      lastTimerCall;
    private AnimationTimer            timer;


    // ******************** Constructors **************************************
    public RoundLcdClock() {
        hColor        = new SimpleObjectProperty<>(this, "hourColor", Color.BLACK);
        mColor        = new SimpleObjectProperty<>(this, "minuteColor", Color.rgb(0, 0, 0, 0.5));
        m5Color       = new SimpleObjectProperty<>(this, "5MinuteColor", Color.BLACK);
        sColor        = new SimpleObjectProperty<>(this, "secondColor", Color.BLACK);
        timeColor     = new SimpleObjectProperty<>(this, "timeColor", Color.BLACK);
        dateColor     = new SimpleObjectProperty<>(this, "dateColor", Color.BLACK);
        alarmOn       = new SimpleBooleanProperty(this, "alarmOn", false);
        alarm         = new SimpleObjectProperty<>(LocalTime.now().minusMinutes(1));
        dateVisible   = new SimpleBooleanProperty(this, "dateVisible", false);
        alarmVisible  = new SimpleBooleanProperty(this, "alarmVisible", false);
        time          = new StringBuilder();
        lastTimerCall = System.nanoTime();
        timer = new AnimationTimer() {
            @Override public void handle(long now) {
                if (now > lastTimerCall + 100_000_000l) {
                    time.setLength(0);
                    pm    = LocalTime.now().get(ChronoField.AMPM_OF_DAY) == 1;
                    hours = LocalTime.now().getHour();
                    String hourString = Integer.toString(hours);
                    if (hours < 10) {
                        time.append("0");
                        time.append(hourString.substring(0, 1));
                    } else {
                        time.append(hourString.substring(0, 1));
                        time.append(hourString.substring(1));
                    }

                    time.append(":");

                    minutes = LocalTime.now().getMinute();
                    String minutesString = Integer.toString(minutes);
                    if (minutes < 10) {
                        time.append("0");
                        time.append(minutesString.substring(0, 1));
                    } else {
                        time.append(minutesString.substring(0, 1));
                        time.append(minutesString.substring(1));
                    }

                    seconds = LocalTime.now().getSecond();

                    if (isAlarmOn() && LocalTime.now().isAfter(getAlarm()) && LocalTime.now().isBefore(getAlarm().plusNanos(105_000_000))) {
                        fireAlarmEvent();
                    }

                    drawForeground();
                    lastTimerCall = now;
                }
            }
        };

        init();
        initGraphics();
        registerListeners();
        timer.start();
    }


    // ******************** Initialization ************************************
    private void init() {
        if (Double.compare(getPrefWidth(), 0.0) <= 0 || Double.compare(getPrefHeight(), 0.0) <= 0 ||
            Double.compare(getWidth(), 0.0) <= 0 || Double.compare(getHeight(), 0.0) <= 0) {
            if (getPrefWidth() > 0 && getPrefHeight() > 0) {
                setPrefSize(getPrefWidth(), getPrefHeight());
            } else {
                setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT);
            }
        }

        if (Double.compare(getMinWidth(), 0.0) <= 0 || Double.compare(getMinHeight(), 0.0) <= 0) {
            setMinSize(MINIMUM_WIDTH, MINIMUM_HEIGHT);
        }

        if (Double.compare(getMaxWidth(), 0.0) <= 0 || Double.compare(getMaxHeight(), 0.0) <= 0) {
            setMaxSize(MAXIMUM_WIDTH, MAXIMUM_HEIGHT);
        }
    }

    private void initGraphics() {
        Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/digital.ttf"), (0.5833333333 * PREFERRED_HEIGHT));         // "Digital-7"
        //Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/digitalreadout.ttf"), (0.5833333333 * PREFERRED_HEIGHT));  // "Digital Readout Upright"


        canvasBkg = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxBkg    = canvasBkg.getGraphicsContext2D();

        canvasFg  = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
        ctxFg     = canvasFg.getGraphicsContext2D();

        pane      = new Pane();
        pane.getChildren().setAll(canvasBkg, canvasFg);

        getChildren().setAll(pane);
        resize();
    }

    private void registerListeners() {
        widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE"));
        heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE"));
        hColor.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        mColor.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        m5Color.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        sColor.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        timeColor.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        dateColor.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        alarmOn.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        dateVisible.addListener(observable -> handleControlPropertyChanged("REDRAW"));
        alarmVisible.addListener(observable -> handleControlPropertyChanged("REDRAW"));
    }


    // ******************** Methods *******************************************
    protected void handleControlPropertyChanged(final String PROPERTY) {
        if ("RESIZE".equals(PROPERTY)) {
            resize();
        } else if ("REDRAW".equals(PROPERTY)) {
            timer.stop();
            drawBackground();
            drawForeground();
            timer.start();
        }
    }

    @Override protected double computePrefWidth(final double PREF_HEIGHT) {
        double prefHeight = PREFERRED_HEIGHT;
        if (PREF_HEIGHT != -1) {
            prefHeight = Math.max(0, PREF_HEIGHT - getInsets().getTop() - getInsets().getBottom());
        }
        return super.computePrefWidth(prefHeight);
    }
    @Override protected double computePrefHeight(final double PREF_WIDTH) {
        double prefWidth = PREFERRED_WIDTH;
        if (PREF_WIDTH != -1) {
            prefWidth = Math.max(0, PREF_WIDTH - getInsets().getLeft() - getInsets().getRight());
        }
        return super.computePrefWidth(prefWidth);
    }

    @Override protected double computeMinWidth(final double MIN_HEIGHT) {
        return super.computeMinWidth(Math.max(MINIMUM_HEIGHT, MIN_HEIGHT - getInsets().getTop() - getInsets().getBottom()));
    }
    @Override protected double computeMinHeight(final double MIN_WIDTH) {
        return super.computeMinHeight(Math.max(MINIMUM_WIDTH, MIN_WIDTH - getInsets().getLeft() - getInsets().getRight()));
    }

    @Override protected double computeMaxWidth(final double MAX_HEIGHT) {
        return super.computeMaxWidth(Math.min(MAXIMUM_HEIGHT, MAX_HEIGHT - getInsets().getTop() - getInsets().getBottom()));
    }
    @Override protected double computeMaxHeight(final double MAX_WIDTH) {
        return super.computeMaxHeight(Math.min(MAXIMUM_WIDTH, MAX_WIDTH - getInsets().getLeft() - getInsets().getRight()));
    }

    public final Color getHourColor() {
        return hColor.get();
    }
    public final void setHourColor(final Color HOUR_COLOR) {
        hColor.set(HOUR_COLOR);
    }
    public final ObjectProperty<Color> hourColorProperty() {
        return hColor;
    }

    public final Color getMinuteColor() {
        return mColor.get();
    }
    public final void setMinuteColor(final Color MINUTE_COLOR) {
        mColor.set(MINUTE_COLOR);
    }
    public final ObjectProperty<Color> minuteColorProperty() {
        return mColor;
    }

    public final Color getMinute5Color() {
        return m5Color.get();
    }
    public final void setMinute5Color(final Color MINUTE_5_COLOR) {
        m5Color.set(MINUTE_5_COLOR);
    }
    public final ObjectProperty<Color> minute5ColorProperty() {
        return m5Color;
    }

    public final Color getSecondColor() {
        return sColor.get();
    }
    public final void setSecondColor(final Color SECOND_COLOR) {
        sColor.set(SECOND_COLOR);
    }
    public final ObjectProperty<Color> secondColorProperty() {
        return sColor;
    }

    public final Color getTimeColor() {
        return timeColor.get();
    }
    public final void setTimeColor(final Color TIME_COLOR) {
        timeColor.set(TIME_COLOR);
    }
    public final ObjectProperty<Color> timeColorProperty() {
        return timeColor;
    }

    public final Color getDateColor() {
        return dateColor.get();
    }
    public final void setDateColor(final Color DATE_COLOR) {
        dateColor.set(DATE_COLOR);
    }
    public final ObjectProperty<Color> dateColorProperty() {
        return dateColor;
    }

    public final boolean isAlarmOn() {
        return alarmOn.get();
    }
    public final void setAlarmOn(final boolean ALARM_ON) {
        alarmOn.set(ALARM_ON);
    }
    public final BooleanProperty alarmOnProperty() {
        return alarmOn;
    }

    public final LocalTime getAlarm() {
        return alarm.get();
    }
    public final void setAlarm(final LocalTime ALARM) {
        alarm.set(ALARM);
    }
    public final ObjectProperty<LocalTime> alarmProperty() {
        return alarm;
    }

    public final boolean isDateVisible() {
        return dateVisible.get();
    }
    public final void setDateVisible(final boolean DATE_VISIBLE) {
        dateVisible.set(DATE_VISIBLE);
    }
    public final BooleanProperty dateVisibleProperty() {
        return dateVisible;
    }

    public final boolean isAlarmVisible() {
        return alarmVisible.get();
    }
    public final void setAlarmVisible(final boolean ALARM_VISIBLE) {
        alarmVisible.set(ALARM_VISIBLE);
    }
    public final BooleanProperty alarmVisibleProperty() {
        return alarmVisible;
    }

    public final void setColor(final Color COLOR) {
        setHourColor(COLOR);
        setMinuteColor(Color.color(COLOR.getRed(), COLOR.getGreen(), COLOR.getBlue(), 0.6));
        setMinute5Color(COLOR);
        setSecondColor(COLOR);
        setTimeColor(COLOR);
        setDateColor(COLOR);
    }

    private void fireAlarmEvent() {
        fireEvent(new AlarmEvent(this, this, AlarmEvent.ALARM));
    }

    private void drawForeground() {
        int hourCounter = 1;
        int minCounter  = 1;
        int secCounter  = 1;

        double strokeWidth = size * 0.06;
        ctxFg.setLineCap(StrokeLineCap.BUTT);
        ctxFg.clearRect(0, 0, size, size);
        for (int i = 450 ; i >= 90 ; i--) {
            ctxFg.save();
            if (i % 6 == 0) {
                // draw minutes
                if (minCounter <= minutes) {
                    ctxFg.setStroke(minCounter % 5 == 0 ? getMinute5Color() :  getMinuteColor());
                    ctxFg.setLineWidth(strokeWidth);
                    ctxFg.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.1, strokeWidth * 0.5 + strokeWidth * 1.1, size - strokeWidth - strokeWidth * 2.2, size - strokeWidth - strokeWidth * 2.2, i + 1 - 6, 4, ArcType.OPEN);
                    minCounter++;
                }
                // draw seconds
                if (secCounter <= seconds + 1) {
                    ctxFg.setStroke(getSecondColor());
                    ctxFg.setLineWidth(strokeWidth * 0.25);
                    ctxFg.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.8, strokeWidth * 0.5 + strokeWidth * 1.8, size - strokeWidth - strokeWidth * 3.6, size - strokeWidth - strokeWidth * 3.6, i + 1 - 6, 4, ArcType.OPEN);
                    secCounter++;
                }
            }
            if (i % 30 == 0) {
                //draw hours
                ctxFg.setStroke(getHourColor());
                ctxFg.setLineWidth(strokeWidth);
                if (hours == 0 || hours == 12) {
                    ctxFg.strokeArc(strokeWidth * 0.5, strokeWidth * 0.5, size - strokeWidth, size - strokeWidth, i + 1 - 30, 28, ArcType.OPEN);
                } else if (hourCounter <= (pm ? hours - 12 : hours)) {
                    ctxFg.strokeArc(strokeWidth * 0.5, strokeWidth * 0.5, size - strokeWidth, size - strokeWidth, i + 1 - 30, 28, ArcType.OPEN);
                    hourCounter++;
                }
            }
            ctxFg.restore();
        }

        // draw the time
        ctxFg.setFill(getTimeColor());
        font = Font.font("Digital-7", (0.2 * size));
        //font = Font.font("Digital Readout Upright", (0.2 * size));
        //font = Font.font("Digital Readout Thick Upright", (0.2 * size));
        ctxFg.setTextBaseline(VPos.CENTER);
        ctxFg.setTextAlign(TextAlignment.CENTER);
        ctxFg.setFont(font);
        ctxFg.fillText(time.toString(), size * 0.5, size * 0.5);

        // draw the date
        if (isDateVisible()) {
            ctxFg.setFill(getDateColor());
            font = Font.font("Digital-7", (0.09 * size));
            ctxFg.setFont(font);
            ctxFg.fillText(LocalDate.now().format(DateTimeFormatter.ISO_DATE), size * 0.5, size * 0.65);
        }

        // draw the alarmOn icon
        if (isAlarmVisible() &&isAlarmOn()) drawAlarmIcon(ctxFg, ctxFg.getFill());
    }

    private void drawBackground() {
        double strokeWidth = size * 0.06;
        ctxBkg.setLineCap(StrokeLineCap.BUTT);
        ctxBkg.clearRect(0, 0, size, size);
        // draw translucent background
        ctxBkg.setStroke(Color.rgb(0, 12, 6, 0.1));
        for (int i = 0 ; i < 360 ; i++) {
            ctxBkg.save();
            if (i % 6 == 0) {
                // draw minutes
                ctxBkg.setStroke(Color.color(mColor.get().getRed(), mColor.get().getGreen(), mColor.get().getBlue(), 0.1));
                ctxBkg.setLineWidth(strokeWidth);
                ctxBkg.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.1, strokeWidth * 0.5 + strokeWidth * 1.1, size - strokeWidth - strokeWidth * 2.2, size - strokeWidth - strokeWidth * 2.2, i + 1, 4, ArcType.OPEN);

                // draw seconds
                ctxBkg.setStroke(Color.color(sColor.get().getRed(), sColor.get().getGreen(), sColor.get().getBlue(), 0.1));
                ctxBkg.setLineWidth(strokeWidth * 0.25);
                ctxBkg.strokeArc(strokeWidth * 0.5 + strokeWidth * 1.8, strokeWidth * 0.5 + strokeWidth * 1.8, size - strokeWidth - strokeWidth * 3.6, size - strokeWidth - strokeWidth * 3.6, i + 1, 4, ArcType.OPEN);
            }
            if (i % 30 == 0) {
                //draw hours
                ctxBkg.setStroke(Color.color(hColor.get().getRed(), hColor.get().getGreen(), hColor.get().getBlue(), 0.1));
                ctxBkg.setLineWidth(strokeWidth);
                ctxBkg.strokeArc(strokeWidth * 0.5, strokeWidth * 0.5, size - strokeWidth, size - strokeWidth, i + 1, 28, ArcType.OPEN);
            }
            ctxBkg.restore();
        }

        // draw the time
        ctxBkg.setFill(Color.color(timeColor.get().getRed(), timeColor.get().getGreen(), timeColor.get().getBlue(), 0.1));
        font = Font.font("Digital-7", (0.2 * size));
        //font = Font.font("Digital Readout Upright", (0.2 * size));
        //font = Font.font("Digital Readout Thick Upright", (0.2 * size));
        ctxBkg.setTextBaseline(VPos.CENTER);
        ctxBkg.setTextAlign(TextAlignment.CENTER);
        ctxBkg.setFont(font);
        ctxBkg.fillText("88:88", size * 0.5, size * 0.5);

        // draw the date
        if (isDateVisible()) {
            ctxBkg.setFill(Color.color(dateColor.get().getRed(), dateColor.get().getGreen(), dateColor.get().getBlue(), 0.1));
            font = Font.font("Digital-7", (0.09 * size));
            ctxBkg.setFont(font);
            ctxBkg.fillText("8888-88-88", size * 0.5, size * 0.65);
        }

        // draw the alarmOn icon
        if (isAlarmVisible() && !isAlarmOn()) drawAlarmIcon(ctxBkg, ctxBkg.getFill());
    }

    private void drawAlarmIcon(final GraphicsContext CTX, final Paint COLOR) {
        double iconSize = 0.1 * size;
        CTX.save();
        CTX.translate((size - iconSize) * 0.5, size * 0.25);
        CTX.beginPath();
        CTX.moveTo(0.6875 * iconSize, 0.875 * iconSize);
        CTX.bezierCurveTo(0.625 * iconSize, 0.9375 * iconSize, 0.5625 * iconSize, iconSize, 0.5 * iconSize, iconSize);
        CTX.bezierCurveTo(0.4375 * iconSize, iconSize, 0.375 * iconSize, 0.9375 * iconSize, 0.375 * iconSize, 0.875 * iconSize);
        CTX.bezierCurveTo(0.375 * iconSize, 0.875 * iconSize, 0.6875 * iconSize, 0.875 * iconSize, 0.6875 * iconSize, 0.875 * iconSize);
        CTX.closePath();
        CTX.moveTo(iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.6875 * iconSize, 0.5625 * iconSize, 0.9375 * iconSize, 0.0, 0.5 * iconSize, 0.0);
        CTX.bezierCurveTo(0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0);
        CTX.bezierCurveTo(0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0, 0.5 * iconSize, 0.0);
        CTX.bezierCurveTo(0.125 * iconSize, 0.0, 0.375 * iconSize, 0.5625 * iconSize, 0.0, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0, 0.8125 * iconSize, 0.0, 0.8125 * iconSize, 0.0625 * iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.0625 * iconSize, 0.8125 * iconSize, 0.5 * iconSize, 0.8125 * iconSize, 0.5 * iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(0.5 * iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.bezierCurveTo(iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize, iconSize, 0.8125 * iconSize);
        CTX.closePath();
        CTX.setFill(COLOR);
        CTX.fill();
        CTX.restore();   
    }
   
    // ******************** Resizing ******************************************
    private void resize() {
        size   = getWidth() < getHeight() ? getWidth() : getHeight();
        width  = getWidth();
        height = getHeight();

        if (width > 0 && height > 0) {
            canvasBkg.setWidth(size);
            canvasBkg.setHeight(size);
            canvasFg.setWidth(size);
            canvasFg.setHeight(size);
            drawBackground();
            drawForeground();
        }
    }
}
TOP

Related Classes of eu.hansolo.enzo.roundlcdclock.RoundLcdClock

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.