/*
* 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.qlocktwo.skin;
import eu.hansolo.enzo.common.BrushedMetalPaint;
import eu.hansolo.enzo.qlocktwo.QlockTwo;
import eu.hansolo.enzo.qlocktwo.QlockWord;
import javafx.animation.AnimationTimer;
import javafx.collections.ListChangeListener;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.Skin;
import javafx.scene.control.SkinBase;
import javafx.scene.image.ImageView;
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 java.time.LocalTime;
public class QlockTwoSkin extends SkinBase<QlockTwo> implements Skin<QlockTwo> {
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 static final double ASPECT_RATIO = 1.0;
private double size;
private double width;
private double height;
private int hour;
private int minute;
private int second;
private QlockTwo.SecondsLeft secondLeft;
private QlockTwo.SecondsRight secondRight;
private int oldMinute;
private int timeZoneOffsetHour;
private int timeZoneOffsetMinute;
private BrushedMetalPaint texture;
private Pane pane;
private Region background;
private ImageView stainlessBackground;
private Region p1;
private Region p2;
private Region p3;
private Region p4;
private Label[][] matrix;
private Region highlight;
private Font font;
private double startX;
private double startY;
private double stepX;
private double stepY;
private long lastTimerCall;
private AnimationTimer timer;
// ******************** Constructors **************************************
public QlockTwoSkin(final QlockTwo CONTROL) {
super(CONTROL);
hour = 0;
minute = 0;
second = 0;
secondLeft = QlockTwo.SecondsLeft.ZERO;
secondRight = QlockTwo.SecondsRight.ZERO;
oldMinute = 0;
timeZoneOffsetHour = 0;
timeZoneOffsetMinute = 0;
texture = new BrushedMetalPaint(Color.web("#888888"));
stainlessBackground = new ImageView();
pane = new Pane();
lastTimerCall = System.nanoTime();
timer = new AnimationTimer() {
@Override public void handle(long now) {
if (now > lastTimerCall + 1_000_000_000l) {
// Hours
hour = LocalTime.now().getHour() - timeZoneOffsetHour;
// Minutes
minute = LocalTime.now().getMinute() + timeZoneOffsetMinute;
if (oldMinute < minute || (oldMinute == 59 && minute == 0)) {
updateClock();
oldMinute = minute;
}
// SecondsRight
if (getSkinnable().isSecondsMode()) {
second = LocalTime.now().getSecond();
if (second < 10) {
secondLeft = QlockTwo.SecondsLeft.ZERO;
secondRight = QlockTwo.SecondsRight.values()[second];
} else {
secondLeft = QlockTwo.SecondsLeft.values()[Integer.parseInt(Integer.toString(second).substring(0, 1))];
secondRight = QlockTwo.SecondsRight.values()[Integer.parseInt(Integer.toString(second).substring(1, 2))];
}
updateClock();
}
lastTimerCall = now;
}
}
};
init();
initGraphics();
registerListeners();
timer.start();
}
// ******************** 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() {
startX = PREFERRED_WIDTH * 0.114;
startY = PREFERRED_WIDTH * 0.112;
stepX = PREFERRED_WIDTH * 0.072;
stepY = PREFERRED_WIDTH * 0.08;
font = Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/din.otf"), PREFERRED_WIDTH * 0.048);
background = new Region();
background.getStyleClass().addAll("background", getSkinnable().getColor().STYLE_CLASS);
stainlessBackground.setImage(texture.getImage(PREFERRED_WIDTH, PREFERRED_HEIGHT));
stainlessBackground.setOpacity(getSkinnable().getColor() == QlockTwo.QlockColor.STAINLESS_STEEL ? 1 : 0);
p1 = new Region();
p1.getStyleClass().add("dot-off");
p2 = new Region();
p2.getStyleClass().add("dot-off");
p3 = new Region();
p3.getStyleClass().add("dot-off");
p4 = new Region();
p4.getStyleClass().add("dot-off");
highlight = new Region();
highlight.getStyleClass().add("highlight");
matrix = new Label[11][10];
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
matrix[x][y] = new Label();
matrix[x][y].setAlignment(Pos.CENTER);
matrix[x][y].setPrefWidth(PREFERRED_WIDTH * 0.048);
matrix[x][y].setPrefHeight(PREFERRED_HEIGHT * 0.048);
matrix[x][y].setText(getSkinnable().getQlock().getMatrix()[y][x]);
matrix[x][y].setFont(font);
matrix[x][y].getStyleClass().add("text-off");
}
}
pane.getChildren().setAll(background,
stainlessBackground,
p4,
p3,
p2,
p1);
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
pane.getChildren().add(matrix[x][y]);
}
}
pane.getChildren().add(highlight);
getChildren().setAll(pane);
resize();
}
private void registerListeners() {
getSkinnable().widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") );
getSkinnable().heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") );
getSkinnable().prefWidthProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE") );
getSkinnable().prefHeightProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE") );
getSkinnable().colorProperty().addListener(observable -> handleControlPropertyChanged("COLOR") );
getSkinnable().languageProperty().addListener(observable -> handleControlPropertyChanged("LANGUAGE") );
getSkinnable().highlightVisibleProperty().addListener(observable -> handleControlPropertyChanged( "HIGHLIGHT") );
getSkinnable().getStyleClass().addListener(new ListChangeListener<String>() {
@Override public void onChanged(Change<? extends String> change) {
resize();
}
});
}
// ******************** Methods *******************************************
protected void handleControlPropertyChanged(final String PROPERTY) {
if ("RESIZE".equals(PROPERTY)) {
resize();
} else if ("COLOR".equals(PROPERTY)) {
background.getStyleClass().setAll("background", getSkinnable().getColor().STYLE_CLASS);
stainlessBackground.setOpacity(getSkinnable().getColor() == QlockTwo.QlockColor.STAINLESS_STEEL ? 1 : 0);
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
matrix[x][y].getStyleClass().setAll("text-off", getSkinnable().getColor().STYLE_CLASS);
}
}
p1.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
p2.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
p3.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
p4.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
} else if ("LANGUAGE".equals(PROPERTY)) {
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
matrix[x][y].setText(getSkinnable().getQlock().getMatrix()[y][x]);
}
}
} else if ("HIGHLIGHT".equals(PROPERTY)) {
highlight.setOpacity(getSkinnable().isHighlightVisible() ? 1 : 0);
}
}
@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);
}
// ******************** Update ********************************************
private void updateClock() {
if (getSkinnable().isSecondsMode()) {
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
if (secondLeft.dots.containsKey(y) || secondRight.dots.containsKey(y)) {
if (secondLeft.dots.containsKey(y) && secondLeft.dots.get(y).contains(x)) {
matrix[x][y].getStyleClass().setAll("text-on", getSkinnable().getColor().STYLE_CLASS);
} else if (secondRight.dots.containsKey(y) && secondRight.dots.get(y).contains(x)) {
matrix[x][y].getStyleClass().setAll("text-on", getSkinnable().getColor().STYLE_CLASS);
} else {
matrix[x][y].getStyleClass().setAll("text-off", getSkinnable().getColor().STYLE_CLASS);
}
} else {
matrix[x][y].getStyleClass().setAll("text-off", getSkinnable().getColor().STYLE_CLASS);
}
}
}
} else {
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
matrix[x][y].getStyleClass().setAll("text-off", getSkinnable().getColor().STYLE_CLASS);
}
}
for (QlockWord word : getSkinnable().getQlock().getTime(minute, hour)) {
for (int col = word.getStart() ; col <= word.getStop() ; col++) {
matrix[col][word.getRow()].getStyleClass().setAll("text-on", getSkinnable().getColor().STYLE_CLASS);
}
}
}
int min = minute > 60 ? minute - 60 : (minute < 0 ? minute + 60 : minute);
if (min %5 == 0) {
p1.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
p2.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
p3.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
p4.getStyleClass().setAll("dot-off", getSkinnable().getColor().STYLE_CLASS);
} else if (min %10 == 1 || min %10 == 6) {
p1.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
} else if (min %10 == 2 || min %10 == 7) {
p1.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
p2.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
} else if (min %10 == 3 || min %10 == 8) {
p1.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
p2.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
p3.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
} else if (min %10 == 4 || min %10 == 9) {
p1.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
p2.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
p3.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
p4.getStyleClass().setAll("dot-on", getSkinnable().getColor().STYLE_CLASS);
}
}
// ******************** Resizing ******************************************
private void resize() {
size = getSkinnable().getWidth() < getSkinnable().getHeight() ? getSkinnable().getWidth() : getSkinnable().getHeight();
width = getSkinnable().getWidth();
height = getSkinnable().getHeight();
if (width > height) {
width = 1 / (ASPECT_RATIO / height);
} else if (1 / (ASPECT_RATIO / height) > width) {
height = width;
}
if (width > 0 && height > 0) {
background.setPrefSize(width, height);
if (width != 0 && height != 0) {
stainlessBackground.setImage(texture.getImage(width, height));
}
p4.setPrefSize(0.012 * width, 0.012 * height);
p4.setTranslateX(0.044 * width);
p4.setTranslateY(0.944 * height);
p3.setPrefSize(0.012 * width, 0.012 * height);
p3.setTranslateX(0.944 * width);
p3.setTranslateY(0.944 * height);
p2.setPrefSize(0.012 * width, 0.012 * height);
p2.setTranslateX(0.944 * width);
p2.setTranslateY(0.044 * height);
p1.setPrefSize(0.012 * width, 0.012 * height);
p1.setTranslateX(0.044 * width);
p1.setTranslateY(0.044 * height);
startX = size * 0.114;
startY = size * 0.112;
stepX = size * 0.072;
stepY = size * 0.08;
font = Font.font("DINfun Pro", FontWeight.NORMAL, FontPosture.REGULAR, size * 0.048);
for (int y = 0 ; y < 10 ; y++) {
for (int x = 0 ; x < 11 ; x++) {
matrix[x][y].setFont(font);
matrix[x][y].setPrefSize(size * 0.048, size * 0.048);
matrix[x][y].setTranslateY(startY + y * stepY);
matrix[x][y].setTranslateX(startX + x * stepX);
matrix[x][y].setTranslateY(startY + y * stepY);
}
}
highlight.setPrefSize(0.8572706909179687 * width, 0.7135147094726563 * height);
highlight.setTranslateX(0.14224906921386718 * width);
highlight.setTranslateY(0.28614569091796876 * height);
}
}
}