/*
* 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.clock.skin;
import eu.hansolo.enzo.clock.Clock;
import eu.hansolo.enzo.common.Fonts;
import javafx.animation.AnimationTimer;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.VPos;
import javafx.scene.Group;
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.DropShadow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.List;
/**
* User: hansolo
* Date: 31.10.12
* Time: 14:18
*/
public class ClockSkin extends SkinBase<Clock> implements Skin<Clock> {
private static final long INTERVAL = 20_000_000l;
private static final double PREFERRED_WIDTH = 200;
private static final double PREFERRED_HEIGHT = 200;
private static final double MINIMUM_WIDTH = 50;
private static final double MINIMUM_HEIGHT = 50;
private static final double MAXIMUM_WIDTH = 1024;
private static final double MAXIMUM_HEIGHT = 1024;
private Pane pane;
private String nightDayStyleClass;
private Region background;
private Canvas logoLayer;
private GraphicsContext ctx;
private Region hourPointer;
private Region hourPointerFlour;
private Region minutePointer;
private Region minutePointerFlour;
private Region secondPointer;
private Region centerKnob;
private Region foreground;
private double size;
private double hourPointerWidthFactor;
private double hourPointerHeightFactor;
private double minutePointerWidthFactor;
private double minutePointerHeightFactor;
private double secondPointerWidthFactor;
private double secondPointerHeightFactor;
private double majorTickWidthFactor;
private double majorTickHeightFactor;
private double minorTickWidthFactor;
private double minorTickHeightFactor;
private double majorTickOffset;
private double minorTickOffset;
private Rotate hourAngle;
private Rotate minuteAngle;
private Rotate secondAngle;
private List<Region> ticks;
private List<Text> tickLabels;
private Group tickMarkGroup;
private Group tickLabelGroup;
private Group pointerGroup;
private Group secondPointerGroup;
private Font tickLabelFont;
private DoubleProperty currentMinuteAngle;
private DoubleProperty minute;
private Timeline timeline;
private long lastTimerCall;
private AnimationTimer timer;
// ******************** Constructors **************************************
public ClockSkin(final Clock CONTROL) {
super(CONTROL);
nightDayStyleClass = getSkinnable().isNightMode() ? "night-mode" : "day-mode";
hourPointerWidthFactor = 0.04;
hourPointerHeightFactor = 0.55;
minutePointerWidthFactor = 0.04;
minutePointerHeightFactor = 0.4;
secondPointerWidthFactor = 0.075;
secondPointerHeightFactor = 0.46;
majorTickWidthFactor = 0.04;
majorTickHeightFactor = 0.12;
minorTickWidthFactor = 0.01;
minorTickHeightFactor = 0.05;
majorTickOffset = 0.018;
minorTickOffset = 0.05;
//tickLabelFont = Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/helvetica.ttf"), 12);
tickLabelFont = Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/bebasneue.ttf"), 12);
minute = new SimpleDoubleProperty(0);
currentMinuteAngle = new SimpleDoubleProperty(0);
hourAngle = new Rotate();
hourAngle.angleProperty().bind(currentMinuteAngle);
minuteAngle = new Rotate();
secondAngle = new Rotate();
ticks = new ArrayList<>(60);
tickLabels = new ArrayList<>(12);
timeline = new Timeline();
timer = new AnimationTimer() {
@Override public void handle(final long NOW) {
if (NOW >= lastTimerCall + INTERVAL) {
updateTime(LocalDateTime.now().plus(getSkinnable().getOffset()));
lastTimerCall = NOW;
}
}
};
minute.addListener(observable -> moveMinutePointer(minute.get()) );
init();
initGraphics();
registerListeners();
if (getSkinnable().isRunning()) {
timer.start();
} else {
updateTime(LocalDateTime.now().plus(getSkinnable().getOffset()));
}
}
// ******************** 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);
}
}
private void initGraphics() {
pane = new Pane();
background = new Region();
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
background.getStyleClass().setAll("background-ios6");
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
background.getStyleClass().setAll("background-db");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
background.getStyleClass().setAll("background-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
background.getStyleClass().setAll("background-bosch");
}
logoLayer = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT);
ctx = logoLayer.getGraphicsContext2D();
String majorTickStyleClass;
String minorTickStyleClass;
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
majorTickStyleClass = "major-tick-ios6";
minorTickStyleClass = "minor-tick-ios6";
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
majorTickStyleClass = "major-tick-db";
minorTickStyleClass = "minor-tick-db";
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
majorTickStyleClass = "major-tick-bosch";
minorTickStyleClass = "minor-tick-bosch";
} else {
majorTickStyleClass = "major-tick-braun";
minorTickStyleClass = "minor-tick-braun";
}
int tickLabelCounter = 1;
for (double angle = 0 ; angle < 360 ; angle += 6) {
Region tick = new Region();
if (angle % 30 == 0) {
tick.getStyleClass().setAll(majorTickStyleClass);
Text tickLabel = new Text(Integer.toString(tickLabelCounter));
tickLabel.getStyleClass().setAll("tick-label-braun");
tickLabels.add(tickLabel);
tickLabelCounter++;
} else {
tick.getStyleClass().setAll(minorTickStyleClass);
}
ticks.add(tick);
}
DropShadow dropShadow = new DropShadow();
dropShadow.setColor(Color.rgb(0, 0, 0, 0.65));
dropShadow.setRadius(1.5);
dropShadow.setBlurType(BlurType.TWO_PASS_BOX);
dropShadow.setOffsetY(1);
tickMarkGroup = new Group();
tickMarkGroup.setEffect(dropShadow);
tickMarkGroup.getChildren().setAll(ticks);
tickLabelGroup = new Group();
tickLabelGroup.setEffect(dropShadow);
tickLabelGroup.getChildren().setAll(tickLabels);
tickLabelGroup.setOpacity(Clock.Design.BRAUN == getSkinnable().getDesign() ? 1 : 0);
hourPointer = new Region();
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
hourPointer.getStyleClass().setAll("hour-pointer-ios6");
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
hourPointer.getStyleClass().setAll("hour-pointer-db");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
hourPointer.getStyleClass().setAll("hour-pointer-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
hourPointer.getStyleClass().setAll("hour-pointer-bosch");
}
hourPointer.getTransforms().setAll(hourAngle);
hourPointerFlour = new Region();
hourPointerFlour.getStyleClass().setAll("hour-pointer-braun-flour");
if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
hourPointerFlour.setOpacity(1);
} else {
hourPointerFlour.setOpacity(0);
}
hourPointerFlour.getTransforms().setAll(hourAngle);
minutePointer = new Region();
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
minutePointer.getStyleClass().setAll("minute-pointer-ios6");
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
minutePointer.getStyleClass().setAll("minute-pointer-db");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
minutePointer.getStyleClass().setAll("minute-pointer-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
minutePointer.getStyleClass().setAll("minute-pointer-bosch");
}
minutePointer.getTransforms().setAll(minuteAngle);
minutePointerFlour = new Region();
minutePointerFlour.getStyleClass().setAll("minute-pointer-braun-flour");
if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
minutePointerFlour.setOpacity(1);
} else {
minutePointerFlour.setOpacity(0);
}
minutePointerFlour.getTransforms().setAll(minuteAngle);
DropShadow pointerShadow = new DropShadow();
pointerShadow.setColor(Color.rgb(0, 0, 0, 0.45));
pointerShadow.setRadius(12);
pointerShadow.setBlurType(BlurType.TWO_PASS_BOX);
pointerShadow.setOffsetY(6);
pointerGroup = new Group();
pointerGroup.setEffect(pointerShadow);
pointerGroup.getChildren().setAll(minutePointerFlour, minutePointer, hourPointerFlour, hourPointer);
secondPointer = new Region();
secondPointer.setOpacity(1);
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
secondPointer.getStyleClass().setAll("second-pointer-ios6");
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
secondPointer.getStyleClass().setAll("second-pointer-db");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
secondPointer.getStyleClass().setAll("second-pointer-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
secondPointer.setOpacity(0);
}
secondPointer.getTransforms().setAll(secondAngle);
InnerShadow secondPointerInnerShadow = new InnerShadow();
secondPointerInnerShadow.setColor(Color.rgb(0, 0, 0, 0.3));
secondPointerInnerShadow.setRadius(1);
secondPointerInnerShadow.setBlurType(BlurType.TWO_PASS_BOX);
secondPointerInnerShadow.setOffsetY(-1);
InnerShadow secondPointerInnerHighlight = new InnerShadow();
secondPointerInnerHighlight.setColor(Color.rgb(255, 255, 255, 0.3));
secondPointerInnerHighlight.setRadius(1);
secondPointerInnerHighlight.setBlurType(BlurType.TWO_PASS_BOX);
secondPointerInnerHighlight.setOffsetY(1);
secondPointerInnerHighlight.setInput(secondPointerInnerShadow);
DropShadow secondPointerShadow = new DropShadow();
secondPointerShadow.setColor(Color.rgb(0, 0, 0, 0.45));
secondPointerShadow.setRadius(12);
secondPointerShadow.setBlurType(BlurType.TWO_PASS_BOX);
secondPointerShadow.setOffsetY(6);
secondPointerShadow.setInput(secondPointerInnerHighlight);
secondPointerGroup = new Group();
secondPointerGroup.setEffect(secondPointerShadow);
secondPointerGroup.getChildren().setAll(secondPointer);
secondPointerGroup.setOpacity(getSkinnable().isSecondPointerVisible() ? 1 : 0);
centerKnob = new Region();
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
centerKnob.getStyleClass().setAll("center-knob-ios6");
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
centerKnob.getStyleClass().setAll("center-knob-db");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
centerKnob.getStyleClass().setAll("center-knob-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
centerKnob.getStyleClass().setAll("center-knob-bosch");
}
foreground = new Region();
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
foreground.getStyleClass().setAll("foreground-ios6");
} else if (Clock.Design.DB == getSkinnable().getDesign()) {
foreground.getStyleClass().setAll("foreground-db");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
foreground.getStyleClass().setAll("foreground-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
foreground.getStyleClass().setAll("foreground-bosch");
}
foreground.setOpacity(getSkinnable().isHighlightVisible() ? 1 : 0);
pane.getChildren().setAll(background, logoLayer, tickMarkGroup, tickLabelGroup, pointerGroup, secondPointerGroup, centerKnob, foreground);
getChildren().setAll(pane);
updateDesign();
}
private void registerListeners() {
getSkinnable().widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") );
getSkinnable().heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") );
getSkinnable().secondPointerVisibleProperty().addListener(observable -> handleControlPropertyChanged("SECOND_POINTER_VISIBLE") );
getSkinnable().nightModeProperty().addListener(observable -> handleControlPropertyChanged("DESIGN") );
getSkinnable().designProperty().addListener(observable -> handleControlPropertyChanged("DESIGN") );
getSkinnable().highlightVisibleProperty().addListener(observable -> handleControlPropertyChanged("DESIGN") );
getSkinnable().dateTimeProperty().addListener(observable -> handleControlPropertyChanged("DATE_TIME"));
getSkinnable().runningProperty().addListener(observable -> handleControlPropertyChanged("RUNNING"));
}
// ******************** Methods *******************************************
protected void handleControlPropertyChanged(final String PROPERTY) {
if ("RESIZE".equals(PROPERTY)) {
resize();
} else if ("DESIGN".equals(PROPERTY)) {
updateDesign();
} else if ("SECOND_POINTER_VISIBLE".equals(PROPERTY)) {
secondPointerGroup.setOpacity(getSkinnable().isSecondPointerVisible() ? 1 : 0);
} else if ("DATE_TIME".equals(PROPERTY)) {
if (getSkinnable().isRunning()) updateTime(getSkinnable().getDateTime().plus(getSkinnable().getOffset()));
} else if ("RUNNING".equals(PROPERTY)) {
if (getSkinnable().isRunning()) {
timer.start();
} else {
timer.stop();
}
}
}
@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, 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);
}
private void updateTime(final LocalDateTime DATE_TIME) {
// Seconds
if (getSkinnable().isDiscreteSecond()) {
secondAngle.setAngle(DATE_TIME.getSecond() * 6);
} else {
secondAngle.setAngle(DATE_TIME.getSecond() * 6 + DATE_TIME.get(ChronoField.MILLI_OF_SECOND) * 0.006);
}
// Minutes
minute.set(DATE_TIME.getMinute() * 6);
// Hours
minuteAngle.setAngle(DATE_TIME.getHour() * 30 + 0.5 * DATE_TIME.getMinute());
if (getSkinnable().isAutoNightMode()) checkForNight(DATE_TIME);
}
private void checkForNight(final LocalDateTime DATE_TIME) {
int hour = DATE_TIME.getHour();
int minute = DATE_TIME.getMinute();
if (0 <= hour && minute >= 0 && hour <= 5 && minute <= 59|| 17 <= hour && minute <= 59 && hour <= 23 && minute <= 59) {
getSkinnable().setNightMode(true);
} else {
getSkinnable().setNightMode(false);
}
}
private void drawLogoLayer() {
ctx.clearRect(0, 0, size, size);
if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
ctx.setFill(getSkinnable().isNightMode() ? Color.rgb(240, 240, 240) : Color.rgb(10, 10, 10));
ctx.fillRect(size * 0.5 - 1, size * 0.18, 2, size * 0.27);
ctx.fillRect(size * 0.5 - 1, size * 0.55, 2, size * 0.27);
ctx.fillRect(size * 0.18, size * 0.5 - 1, size * 0.27, 2);
ctx.fillRect(size * 0.55, size * 0.5 - 1, size * 0.27, 2);
}
if (getSkinnable().getText().isEmpty()) return;
ctx.setFill(getSkinnable().isNightMode() ? Color.WHITE : Color.BLACK);
ctx.setFont(Fonts.opensansSemiBold(size * 0.05));
ctx.setTextBaseline(VPos.CENTER);
ctx.setTextAlign(TextAlignment.CENTER);
ctx.fillText(getSkinnable().getText(), size * 0.5, size * 0.675, size * 0.8);
}
private void updateDesign() {
// Set day or night mode
nightDayStyleClass = getSkinnable().isNightMode() ? "night-mode" : "day-mode";
// Set Styles for each component
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
background.getStyleClass().setAll(nightDayStyleClass, "background-ios6");
int index = 0;
for (double angle = 0 ; angle < 360 ; angle += 6) {
Region tick = ticks.get(index);
if (angle % 30 == 0) {
tick.getStyleClass().setAll(nightDayStyleClass, "major-tick-ios6");
} else {
tick.getStyleClass().setAll(nightDayStyleClass, "minor-tick-ios6");
}
ticks.add(tick);
index++;
}
hourPointer.getStyleClass().setAll(nightDayStyleClass, "hour-pointer-ios6");
minutePointer.getStyleClass().setAll(nightDayStyleClass, "minute-pointer-ios6");
secondPointer.getStyleClass().setAll(nightDayStyleClass, "second-pointer-ios6");
centerKnob.getStyleClass().setAll(nightDayStyleClass, "center-knob-ios6");
foreground.getStyleClass().setAll(nightDayStyleClass, "foreground-ios6");
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
nightDayStyleClass = getSkinnable().isNightMode() ? "night-mode-braun" : "day-mode-braun";
background.getStyleClass().setAll(nightDayStyleClass, "background-braun");
int index = 0;
for (double angle = 0 ; angle < 360 ; angle += 6) {
if (angle % 30 == 0) {
ticks.get(index).getStyleClass().setAll(nightDayStyleClass, "major-tick-braun");
} else {
ticks.get(index).getStyleClass().setAll(nightDayStyleClass, "minor-tick-braun");
}
index++;
}
for (index = 0 ; index < 12 ; index++) {
tickLabels.get(index).getStyleClass().setAll(nightDayStyleClass, "tick-label-braun");
}
hourPointer.getStyleClass().setAll(nightDayStyleClass, "hour-pointer-braun");
minutePointer.getStyleClass().setAll(nightDayStyleClass, "minute-pointer-braun");
secondPointer.getStyleClass().setAll(nightDayStyleClass, "second-pointer-braun");
centerKnob.getStyleClass().setAll(nightDayStyleClass, "center-knob-braun");
foreground.getStyleClass().setAll(nightDayStyleClass, "foreground-braun");
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
nightDayStyleClass = getSkinnable().isNightMode() ? "night-mode-bosch" : "day-mode-bosch";
background.getStyleClass().setAll(nightDayStyleClass, "background-bosch");
int index = 0;
for (double angle = 0 ; angle < 360 ; angle += 6) {
Region tick = ticks.get(index);
if (angle % 30 == 0) {
tick.getStyleClass().setAll(nightDayStyleClass, "major-tick-bosch");
} else {
tick.getStyleClass().setAll(nightDayStyleClass, "minor-tick-bosch");
}
ticks.add(tick);
index++;
}
hourPointer.getStyleClass().setAll(nightDayStyleClass, "hour-pointer-bosch");
minutePointer.getStyleClass().setAll(nightDayStyleClass, "minute-pointer-bosch");
secondPointer.getStyleClass().setAll(nightDayStyleClass, "second-pointer-bosch");
centerKnob.getStyleClass().setAll(nightDayStyleClass, "center-knob-bosch");
foreground.getStyleClass().setAll(nightDayStyleClass, "foreground-bosch");
} else {
background.getStyleClass().setAll(nightDayStyleClass, "background-db");
int index = 0;
for (double angle = 0 ; angle < 360 ; angle += 6) {
Region tick = ticks.get(index);
if (angle % 30 == 0) {
tick.getStyleClass().setAll(nightDayStyleClass, "major-tick-db");
} else {
tick.getStyleClass().setAll(nightDayStyleClass, "minor-tick-db");
}
ticks.add(tick);
index++;
}
hourPointer.getStyleClass().setAll(nightDayStyleClass, "hour-pointer-db");
minutePointer.getStyleClass().setAll(nightDayStyleClass, "minute-pointer-db");
secondPointer.getStyleClass().setAll(nightDayStyleClass, "second-pointer-db");
centerKnob.getStyleClass().setAll(nightDayStyleClass, "center-knob-db");
foreground.getStyleClass().setAll(nightDayStyleClass, "foreground-db");
}
tickLabelGroup.setOpacity(Clock.Design.BRAUN == getSkinnable().getDesign() ? 1 : 0);
foreground.setOpacity(getSkinnable().isHighlightVisible() ? 1 : 0);
resize();
}
private void resize() {
size = getSkinnable().getWidth() < getSkinnable().getHeight() ? getSkinnable().getWidth() : getSkinnable().getHeight();
logoLayer.setWidth(size);
logoLayer.setHeight(size);
if (size > 0) {
background.setPrefSize(size, size);
// TODO: hourPointer and minutePointer have to be vice versa...wrong scaling here !!!
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
hourPointerWidthFactor = 0.04;
hourPointerHeightFactor = 0.55;
minutePointerWidthFactor = 0.04;
minutePointerHeightFactor = 0.4;
secondPointerWidthFactor = 0.075;
secondPointerHeightFactor = 0.46;
majorTickWidthFactor = 0.04;
majorTickHeightFactor = 0.12;
minorTickWidthFactor = 0.01;
minorTickHeightFactor = 0.05;
majorTickOffset = 0.018;
minorTickOffset = 0.05;
hourAngle.setPivotX(size * 0.5 * hourPointerWidthFactor);
hourAngle.setPivotY(size * 0.76 * hourPointerHeightFactor);
minuteAngle.setPivotX(size * 0.5 * minutePointerWidthFactor);
minuteAngle.setPivotY(size * 0.66 * minutePointerHeightFactor);
secondAngle.setPivotX(size * 0.5 * secondPointerWidthFactor);
secondAngle.setPivotY(size * 0.7341040462 * secondPointerHeightFactor);
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
hourPointerWidthFactor = 0.105;
hourPointerHeightFactor = 0.485;
minutePointerWidthFactor = 0.105;
minutePointerHeightFactor = 0.4;
secondPointerWidthFactor = 0.09;
secondPointerHeightFactor = 0.53;
majorTickWidthFactor = 0.015;
majorTickHeightFactor = 0.045;
minorTickWidthFactor = 0.0075;
minorTickHeightFactor = 0.0225;
majorTickOffset = 0.012;
minorTickOffset = 0.02;
hourAngle.setPivotX(size * 0.5 * hourPointerWidthFactor);
hourAngle.setPivotY(size * 0.895 * hourPointerHeightFactor);
minuteAngle.setPivotX(size * 0.5 * minutePointerWidthFactor);
minuteAngle.setPivotY(size * 0.87 * minutePointerHeightFactor);
secondAngle.setPivotX(size * 0.5 * secondPointerWidthFactor);
secondAngle.setPivotY(size * 0.8125 * secondPointerHeightFactor);
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
hourPointerWidthFactor = 0.04;
hourPointerHeightFactor = 0.54; // ToDo: switch with minutePointerHeightFactor
minutePointerWidthFactor = 0.04;
minutePointerHeightFactor = 0.38; // ToDo: switch with hourPointerHeightFactor
secondPointerWidthFactor = 0.09;
secondPointerHeightFactor = 0.53;
majorTickWidthFactor = 0.02;
majorTickHeightFactor = 0.145;
minorTickWidthFactor = 0.006;
minorTickHeightFactor = 0.07;
majorTickOffset = 0.005;
minorTickOffset = 0.04;
hourAngle.setPivotX(size * 0.5 * hourPointerWidthFactor);
hourAngle.setPivotY(size * 0.8240740741 * hourPointerHeightFactor);
minuteAngle.setPivotX(size * 0.5 * minutePointerWidthFactor);
minuteAngle.setPivotY(size * 0.75 * minutePointerHeightFactor);
secondAngle.setPivotX(size * 0.5 * secondPointerWidthFactor);
secondAngle.setPivotY(size * 0.8125 * secondPointerHeightFactor);
} else {
hourPointerWidthFactor = 0.04;
hourPointerHeightFactor = 0.47;
minutePointerWidthFactor = 0.055;
minutePointerHeightFactor = 0.33;
secondPointerWidthFactor = 0.1;
secondPointerHeightFactor = 0.455;
majorTickWidthFactor = 0.04;
majorTickHeightFactor = 0.12;
minorTickWidthFactor = 0.025;
minorTickHeightFactor = 0.04;
majorTickOffset = 0.018;
minorTickOffset = 0.06;
hourAngle.setPivotX(size * 0.5 * hourPointerWidthFactor);
hourAngle.setPivotY(size * hourPointerHeightFactor);
minuteAngle.setPivotX(size * 0.5 * minutePointerWidthFactor);
minuteAngle.setPivotY(size * minutePointerHeightFactor);
secondAngle.setPivotX(size * 0.5 * secondPointerWidthFactor);
secondAngle.setPivotY(size * secondPointerHeightFactor);
}
drawLogoLayer();
double radius = 0.4;
double sinValue;
double cosValue;
int index = 0;
for (double angle = 0 ; angle < 360 ; angle += 6) {
sinValue = Math.sin(Math.toRadians(angle));
cosValue = Math.cos(Math.toRadians(angle));
Region tick = ticks.get(index);
if (angle % 30 == 0) {
tick.setPrefWidth(size * majorTickWidthFactor);
tick.setPrefHeight(size * majorTickHeightFactor);
tick.setTranslateX(size * 0.5 + ((size * (radius + majorTickOffset) * sinValue) - (size * (majorTickWidthFactor) * 0.5)));
tick.setTranslateY(size * 0.5 + ((size * (radius + majorTickOffset) * cosValue) - (size * (majorTickHeightFactor) * 0.5)));
} else {
tick.setPrefWidth(size * minorTickWidthFactor);
tick.setPrefHeight(size * minorTickHeightFactor);
tick.setTranslateX(size * 0.5 + ((size * (radius + minorTickOffset) * sinValue) - (size * (minorTickWidthFactor) * 0.5)));
tick.setTranslateY(size * 0.5 + ((size * (radius + minorTickOffset) * cosValue) - (size * (minorTickHeightFactor) * 0.5)));
}
tick.setRotate(-angle);
index++;
}
if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
int tickLabelCounter = 0;
//tickLabelFont = Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/helvetica.ttf"), (0.075 * size));
tickLabelFont = Font.font("Bebas Neue", FontWeight.THIN, FontPosture.REGULAR, 0.09 * size);
for (double angle = 0 ; angle < 360 ; angle += 30.0) {
double x = 0.34 * size * Math.sin(Math.toRadians(150 - angle));
double y = 0.34 * size * Math.cos(Math.toRadians(150 - angle));
tickLabels.get(tickLabelCounter).setFont(tickLabelFont);
tickLabels.get(tickLabelCounter).setX(size * 0.5 + x - tickLabels.get(tickLabelCounter).getLayoutBounds().getWidth() * 0.5);
tickLabels.get(tickLabelCounter).setY(size * 0.5 + y);
tickLabels.get(tickLabelCounter).setTextOrigin(VPos.CENTER);
tickLabels.get(tickLabelCounter).setTextAlignment(TextAlignment.CENTER);
tickLabelCounter++;
}
}
hourPointer.setPrefSize(size * hourPointerWidthFactor, size * hourPointerHeightFactor);
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
hourPointer.setTranslateX(size * 0.5 - (hourPointer.getPrefWidth() * 0.5));
hourPointer.setTranslateY(size * 0.5 - (hourPointer.getPrefHeight()) + (hourPointer.getPrefHeight() * 0.24));
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
hourPointer.setTranslateX(size * 0.5 - (hourPointer.getPrefWidth() * 0.5));
hourPointer.setTranslateY(size * 0.5 - (hourPointer.getPrefHeight()) + (hourPointer.getPrefHeight() * 0.108));
hourPointerFlour.setPrefSize(size * hourPointerWidthFactor, size * hourPointerHeightFactor);
hourPointerFlour.setTranslateX(size * 0.5 - (hourPointer.getPrefWidth() * 0.5));
hourPointerFlour.setTranslateY(size * 0.5 - (hourPointer.getPrefHeight()) + (hourPointer.getPrefHeight() * 0.108));
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
hourPointer.setTranslateX(size * 0.5 - (hourPointer.getPrefWidth() * 0.5));
hourPointer.setTranslateY(size * 0.5 - (hourPointer.getPrefHeight()) + (hourPointer.getPrefHeight() * 0.1759259259));
} else {
hourPointer.setTranslateX(size * 0.5 - (hourPointer.getPrefWidth() * 0.5));
hourPointer.setTranslateY(size * 0.5 - hourPointer.getPrefHeight());
}
minutePointer.setPrefSize(size * minutePointerWidthFactor, size * minutePointerHeightFactor);
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
minutePointer.setTranslateX(size * 0.5 - (minutePointer.getPrefWidth() * 0.5));
minutePointer.setTranslateY(size * 0.5 - (minutePointer.getPrefHeight()) + (minutePointer.getPrefHeight() * 0.34));
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
minutePointer.setTranslateX(size * 0.5 - (minutePointer.getPrefWidth() * 0.5));
minutePointer.setTranslateY(size * 0.5 - (minutePointer.getPrefHeight()) + (minutePointer.getPrefHeight() * 0.128));
minutePointerFlour.setPrefSize(size * minutePointerWidthFactor, size * minutePointerHeightFactor);
minutePointerFlour.setTranslateX(size * 0.5 - (minutePointer.getPrefWidth() * 0.5));
minutePointerFlour.setTranslateY(size * 0.5 - (minutePointer.getPrefHeight()) + (minutePointer.getPrefHeight() * 0.128));
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
minutePointer.setTranslateX(size * 0.5 - (minutePointer.getPrefWidth() * 0.5));
minutePointer.setTranslateY(size * 0.5 - (minutePointer.getPrefHeight()) + (minutePointer.getPrefHeight() * 0.25));
} else {
minutePointer.setTranslateX(size * 0.5 - (minutePointer.getPrefWidth() * 0.5));
minutePointer.setTranslateY(size * 0.5 - minutePointer.getPrefHeight());
}
secondPointer.setPrefSize(size * secondPointerWidthFactor, size * secondPointerHeightFactor);
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
secondPointer.setTranslateX(size * 0.5 - (secondPointer.getPrefWidth() * 0.5));
secondPointer.setTranslateY(size * 0.5 - (secondPointer.getPrefHeight()) + (secondPointer.getPrefHeight() * 0.2658959538));
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
secondPointer.setTranslateX(size * 0.5 - (secondPointer.getPrefWidth() * 0.5));
secondPointer.setTranslateY(size * 0.5 - secondPointer.getPrefHeight() + (secondPointer.getPrefHeight() * 0.189));
} else {
secondPointer.setTranslateX(size * 0.5 - (secondPointer.getPrefWidth() * 0.5));
secondPointer.setTranslateY(size * 0.5 - secondPointer.getPrefHeight());
}
if (Clock.Design.IOS6 == getSkinnable().getDesign()) {
centerKnob.setPrefSize(size * 0.015, size * 0.015);
} else if (Clock.Design.BRAUN == getSkinnable().getDesign()) {
centerKnob.setPrefSize(size * 0.085, size * 0.085);
} else if (Clock.Design.BOSCH == getSkinnable().getDesign()) {
centerKnob.setPrefSize(size * 0.035, size * 0.035);
} else {
centerKnob.setPrefSize(size * 0.1, size * 0.1);
}
centerKnob.setTranslateX(size * 0.5 - (centerKnob.getPrefWidth() * 0.5));
centerKnob.setTranslateY(size * 0.5 - (centerKnob.getPrefHeight() * 0.5));
foreground.setPrefSize(size * 0.955, size * 0.495);
foreground.setTranslateX(size * 0.5 - (foreground.getPrefWidth() * 0.5));
foreground.setTranslateY(size * 0.01);
}
}
// ******************** Drawing related ***********************************
private void moveMinutePointer(double newAngle) {
final KeyValue kv = new KeyValue(currentMinuteAngle, newAngle, Interpolator.SPLINE(0.5, 0.4, 0.4, 1.0));
final KeyFrame kf = new KeyFrame(Duration.millis(200), kv);
timeline = new Timeline();
timeline.getKeyFrames().add(kf);
timeline.play();
}
}