/* * 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); } } }