/* * 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.lcd.skin; import eu.hansolo.enzo.lcd.Alarm; import eu.hansolo.enzo.lcd.LcdClock; import javafx.collections.ListChangeListener; import javafx.geometry.VPos; import javafx.scene.Group; 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.image.Image; import javafx.scene.image.ImageView; import javafx.scene.image.PixelWriter; import javafx.scene.image.WritableImage; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import java.time.format.DateTimeFormatter; import java.time.format.TextStyle; import java.util.Random; /** * Created by * User: hansolo * Date: 19.08.13 * Time: 08:14 */ public class LcdClockSkin extends SkinBase<LcdClock> implements Skin<LcdClock> { private static final double PREFERRED_WIDTH = 76; private static final double PREFERRED_HEIGHT = 40; private static final double MINIMUM_WIDTH = 5; private static final double MINIMUM_HEIGHT = 5; private static final double MAXIMUM_WIDTH = 1024; private static final double MAXIMUM_HEIGHT = 1024; private static final Color DARK_NOISE_COLOR = Color.rgb(100, 100, 100, 0.10); private static final Color BRIGHT_NOISE_COLOR = Color.rgb(200, 200, 200, 0.05); private static final DropShadow FOREGROUND_SHADOW = new DropShadow(); private static double aspectRatio = PREFERRED_HEIGHT / PREFERRED_WIDTH; private static Text oneTimeSegment = new Text("8"); private static Text oneSecondSegment = new Text("8"); private double width; private double height; private Pane pane; private Region main; private ImageView crystalOverlay; private Image crystalImage; private Rectangle crystalClip; private InnerShadow mainInnerShadow0; private InnerShadow mainInnerShadow1; private Region alarm; private Text timeText; private Text backgroundTimeText; private Text secondText; private Text backgroundSecondText; private Text title; private Text dateText; private Text dayOfWeekText; private DateTimeFormatter dateFormat; private double valueOffsetRight; private double digitalFontSizeFactor; private Font timeFont; private Font secondFont; private Font titleFont; private Font smallFont; private StringBuilder backgroundTextBuilder; private Group shadowGroup; // ******************** Constructors ************************************** public LcdClockSkin(final LcdClock CONTROL) { super(CONTROL); aspectRatio = PREFERRED_HEIGHT / PREFERRED_WIDTH; valueOffsetRight = 0.0; digitalFontSizeFactor = 1.0; backgroundTextBuilder = new StringBuilder(); FOREGROUND_SHADOW.setOffsetX(0); FOREGROUND_SHADOW.setOffsetY(1); FOREGROUND_SHADOW.setColor(Color.rgb(0, 0, 0, 0.5)); FOREGROUND_SHADOW.setBlurType(BlurType.TWO_PASS_BOX); FOREGROUND_SHADOW.setRadius(2); adjustDateFormat(); init(); initGraphics(); registerListeners(); } // ******************** 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() { // load the fonts 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" Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/digitalreadoutb.ttf"), (0.5833333333 * PREFERRED_HEIGHT)); // "Digital Readout Thick Upright" Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/elektra.ttf"), (0.58333333 * PREFERRED_HEIGHT)); // "Elektra" Font.loadFont(getClass().getResourceAsStream("/eu/hansolo/enzo/fonts/opensans-semibold.ttf"), (0.58333333 * PREFERRED_HEIGHT)); // "OpenSans" main = new Region(); main.getStyleClass().setAll("main"); main.setOpacity(getSkinnable().isBackgroundVisible() ? 1 : 0); mainInnerShadow0 = new InnerShadow(); mainInnerShadow0.setOffsetX(0.0); mainInnerShadow0.setOffsetY(0.0); mainInnerShadow0.setRadius(3.0 / 132.0 * PREFERRED_WIDTH); mainInnerShadow0.setColor(Color.web("0xffffff80")); mainInnerShadow0.setBlurType(BlurType.TWO_PASS_BOX); mainInnerShadow1 = new InnerShadow(); mainInnerShadow1.setOffsetX(0.0); mainInnerShadow1.setOffsetY(1.0); mainInnerShadow1.setRadius(2.0 / 132.0 * PREFERRED_WIDTH); mainInnerShadow1.setColor(Color.web("0x000000a6")); mainInnerShadow1.setBlurType(BlurType.TWO_PASS_BOX); mainInnerShadow1.setInput(mainInnerShadow0); main.setEffect(getSkinnable().isMainInnerShadowVisible() ? mainInnerShadow1 : null); crystalClip = new Rectangle(0, 0, PREFERRED_WIDTH, PREFERRED_HEIGHT); crystalClip.setArcWidth(5); crystalClip.setArcHeight(5); crystalImage = createNoiseImage(PREFERRED_WIDTH, PREFERRED_HEIGHT, DARK_NOISE_COLOR, BRIGHT_NOISE_COLOR, 8); crystalOverlay = new ImageView(crystalImage); crystalOverlay.setClip(this.crystalClip); crystalOverlay.setOpacity(getSkinnable().isCrystalOverlayVisible() ? 1 : 0); alarm = new Region(); alarm.getStyleClass().setAll("alarm"); alarm.setOpacity(getSkinnable().getAlarms().isEmpty() || allAlarmsInactive() ? 0 : 1); backgroundTimeText = new Text(""); backgroundTimeText.getStyleClass().setAll("fg-trsp"); backgroundTimeText.setOpacity((LcdClock.LcdFont.LCD == getSkinnable().getTimeFont() || LcdClock.LcdFont.ELEKTRA == getSkinnable().getTimeFont()) ? 1 : 0); timeText = new Text(""); timeText.getStyleClass().setAll("fg"); backgroundSecondText = new Text(""); backgroundSecondText.getStyleClass().setAll("fg-trsp"); backgroundSecondText.setOpacity((LcdClock.LcdFont.LCD == getSkinnable().getTimeFont() || LcdClock.LcdFont.ELEKTRA == getSkinnable().getTimeFont()) ? 1 : 0); secondText = new Text(""); secondText.getStyleClass().setAll("fg"); title = new Text(getSkinnable().getTitle()); title.getStyleClass().setAll("fg"); dateText = new Text(getSkinnable().getTime().getMonthValue() + "/" + getSkinnable().getTime().getDayOfMonth() + "/" + getSkinnable().getTime().getYear()); dateText.getStyleClass().setAll("fg"); dayOfWeekText = new Text(""); dayOfWeekText.getStyleClass().setAll("fg"); shadowGroup = new Group(); shadowGroup.setEffect(getSkinnable().isForegroundShadowVisible() ? FOREGROUND_SHADOW : null); shadowGroup.getChildren().setAll(alarm, timeText, secondText, title, dateText, dayOfWeekText); pane = new Pane(); pane.getChildren().setAll(main, crystalOverlay, backgroundTimeText, backgroundSecondText, shadowGroup); getChildren().setAll(pane); resize(); updateLcd(); } private void registerListeners() { getSkinnable().widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE")); getSkinnable().heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE")); getSkinnable().clockProperty().addListener(observable -> handleControlPropertyChanged("UPDATE")); getSkinnable().timeProperty().addListener(observable -> handleControlPropertyChanged("UPDATE")); getSkinnable().titleProperty().addListener(observable -> handleControlPropertyChanged("UPDATE")); getSkinnable().localeProperty().addListener(observable -> handleControlPropertyChanged("UPDATE")); getSkinnable().prefWidthProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE")); getSkinnable().prefHeightProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE")); getSkinnable().timeFontProperty().addListener(observable -> handleControlPropertyChanged("FONT")); getSkinnable().smallFontProperty().addListener(observable -> handleControlPropertyChanged("FONT")); getSkinnable().backgroundVisibleProperty().addListener(observable -> handleControlPropertyChanged("BACKGROUND_VISIBLE")); getSkinnable().crystalOverlayVisibleProperty().addListener(observable -> handleControlPropertyChanged("CRYSTAL_OVERLAY_VISIBLE")); getSkinnable().mainInnerShadowVisibleProperty().addListener(observable -> handleControlPropertyChanged("MAIN_INNER_SHADOW_VISIBLE")); getSkinnable().foregroundShadowVisibleProperty().addListener(observable -> handleControlPropertyChanged("FOREGROUND_SHADOW_VISIBLE")); getSkinnable().dateFormatProperty().addListener(observable -> handleControlPropertyChanged("DATE_FORMAT")); getSkinnable().dateSeparatorProperty().addListener(observable -> handleControlPropertyChanged("DATE_FORMAT")); getSkinnable().getAlarms().addListener((ListChangeListener<Alarm>) change -> handleControlPropertyChanged("ALARM")); getSkinnable().getStyleClass().addListener((ListChangeListener<String>) change -> { resize(); updateLcd(); }); } // ******************** Methods ******************************************* protected void handleControlPropertyChanged(final String PROPERTY) { if ("UPDATE".equals(PROPERTY)) { updateLcd(); } else if ("RESIZE".equals(PROPERTY)) { resize(); updateLcd(); } else if ("PREF_SIZE".equals(PROPERTY)) { aspectRatio = getSkinnable().getPrefHeight() / getSkinnable().getPrefWidth(); } else if ("BACKGROUND_VISIBLE".equals(PROPERTY)) { main.setOpacity(getSkinnable().isBackgroundVisible() ? 1 : 0); crystalOverlay.setOpacity(getSkinnable().isBackgroundVisible() ? 1 : 0); } else if ("CRYSTAL_OVERLAY_VISIBLE".equals(PROPERTY)) { crystalOverlay.setOpacity(getSkinnable().isCrystalOverlayVisible() ? 1 : 0); resize(); } else if ("MAIN_INNER_SHADOW_VISIBLE".equals(PROPERTY)) { main.setEffect(getSkinnable().isMainInnerShadowVisible() ? mainInnerShadow1 : null); } else if ("FOREGROUND_SHADOW_VISIBLE".equals(PROPERTY)) { shadowGroup.setEffect(getSkinnable().isForegroundShadowVisible() ? FOREGROUND_SHADOW : null); } else if ("FONT".equals(PROPERTY)) { updateFonts(); } else if ("ALARM".equals(PROPERTY)) { alarm.setOpacity(getSkinnable().getAlarms().isEmpty() || allAlarmsInactive() ? 0 : 1); } else if ("DATE_FORMAT".equals(PROPERTY)) { adjustDateFormat(); updateLcd(); } } @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 Methods *********************************** private String ensureTwoDigits(final int NUMBER) { if (NUMBER < 10) return "0" + NUMBER; return Integer.toString(NUMBER); } private void adjustDateFormat() { switch (getSkinnable().getDateFormat()) { case DAY_MONTH_YEAR: dateFormat = DateTimeFormatter.ofPattern("dd" + getSkinnable().getDateSeparator().chr + "MM" + getSkinnable().getDateSeparator().chr + "yyyy"); break; case YEAR_MONTH_DAY: default: dateFormat = DateTimeFormatter.ofPattern("yyyy" + getSkinnable().getDateSeparator().chr + "MM" + getSkinnable().getDateSeparator().chr + "dd"); break; } } private boolean allAlarmsInactive() { boolean allAlarmsInactive = true; for (Alarm alarm : getSkinnable().getAlarms()) { if (alarm.isArmed()) { allAlarmsInactive = false; break; } } return allAlarmsInactive; } private Image createNoiseImage(final double WIDTH, final double HEIGHT, final Color DARK_COLOR, final Color BRIGHT_COLOR, final double ALPHA_VARIATION_IN_PERCENT) { int width = WIDTH <= 0 ? (int) PREFERRED_WIDTH : (int) WIDTH; int height = HEIGHT <= 0 ? (int) PREFERRED_HEIGHT : (int) HEIGHT; double alphaVariationInPercent = getSkinnable().clamp(0, 100, ALPHA_VARIATION_IN_PERCENT); final WritableImage IMAGE = new WritableImage(width, height); final PixelWriter PIXEL_WRITER = IMAGE.getPixelWriter(); final Random BW_RND = new Random(); final Random ALPHA_RND = new Random(); final double ALPHA_START = alphaVariationInPercent / 100 / 2; final double ALPHA_VARIATION = alphaVariationInPercent / 100; Color noiseColor; double noiseAlpha; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { noiseColor = BW_RND.nextBoolean() == true ? BRIGHT_COLOR : DARK_COLOR; noiseAlpha = getSkinnable().clamp(0, 1, ALPHA_START + ALPHA_RND.nextDouble() * ALPHA_VARIATION); PIXEL_WRITER.setColor(x, y, Color.color(noiseColor.getRed(), noiseColor.getGreen(), noiseColor.getBlue(), noiseAlpha)); } } return IMAGE; } private void updateFonts() { digitalFontSizeFactor = 1.0; switch(getSkinnable().getTimeFont()) { case LCD: digitalFontSizeFactor = 1.05; timeFont = Font.font("Digital-7", (0.7 * height)); secondFont = Font.font("Digital-7", FontWeight.NORMAL, (0.2 * height)); break; case DIGITAL: digitalFontSizeFactor = 0.7; timeFont = Font.font("Digital Readout Upright", (0.7 * height)); secondFont = Font.font("Digital Readout Upright", FontWeight.NORMAL, (0.2 * height)); break; case DIGITAL_BOLD: digitalFontSizeFactor = 0.7; timeFont = Font.font("Digital Readout Thick Upright", (0.7 * height)); secondFont = Font.font("Digital Readout Thick Upright", FontWeight.NORMAL, (0.2 * height)); break; case ELEKTRA: digitalFontSizeFactor = 0.8; timeFont = Font.font("Elektra", (0.7 * height)); secondFont = Font.font("Elektra", FontWeight.NORMAL, (0.186 * height)); break; case STANDARD: default: digitalFontSizeFactor = 0.7; timeFont = Font.font("Open Sans", FontWeight.NORMAL, (0.6 * height)); secondFont = Font.font("Open Sans", FontWeight.NORMAL, (0.2 * height)); break; } backgroundTimeText.setFont(timeFont); backgroundTimeText.setOpacity((LcdClock.LcdFont.LCD == getSkinnable().getTimeFont() || LcdClock.LcdFont.DIGITAL == getSkinnable().getTimeFont() || LcdClock.LcdFont.DIGITAL_BOLD == getSkinnable().getTimeFont() || LcdClock.LcdFont.ELEKTRA == getSkinnable().getTimeFont()) ? 1 : 0); backgroundSecondText.setFont(secondFont); backgroundSecondText.setOpacity((LcdClock.LcdFont.LCD == getSkinnable().getTimeFont() || LcdClock.LcdFont.DIGITAL == getSkinnable().getTimeFont() || LcdClock.LcdFont.DIGITAL_BOLD == getSkinnable().getTimeFont() || LcdClock.LcdFont.ELEKTRA == getSkinnable().getTimeFont()) ? 1 : 0); timeText.setFont(timeFont); titleFont = Font.font(getSkinnable().getTitleFont(), FontWeight.BOLD, (0.15 * height)); smallFont = Font.font(getSkinnable().getSmallFont(), FontWeight.NORMAL, (0.12 * height)); } private void updateBackgroundText() { // Setup the semitransparent background timeText backgroundTimeText.setTextOrigin(VPos.BASELINE); backgroundTimeText.setTextAlignment(TextAlignment.RIGHT); backgroundSecondText.setTextOrigin(VPos.BASELINE); backgroundSecondText.setTextAlignment(TextAlignment.RIGHT); // Setup the semitransparent background timeText // Width of one time segment oneTimeSegment.setFont(timeFont); if (LcdClock.LcdFont.LCD == getSkinnable().getTimeFont()) { oneTimeSegment.setText("8"); } else if (LcdClock.LcdFont.DIGITAL == getSkinnable().getTimeFont()) { oneTimeSegment.setText("_"); } else if (LcdClock.LcdFont.DIGITAL_BOLD == getSkinnable().getTimeFont()) { oneTimeSegment.setText("_"); } else if (LcdClock.LcdFont.ELEKTRA == getSkinnable().getTimeFont()) { oneTimeSegment.setText("_"); } // Width of one time segment oneSecondSegment.setFont(secondFont); if (LcdClock.LcdFont.LCD == getSkinnable().getTimeFont()) { oneSecondSegment.setText("8"); } else if (LcdClock.LcdFont.DIGITAL == getSkinnable().getTimeFont()) { oneSecondSegment.setText("_"); } else if (LcdClock.LcdFont.DIGITAL_BOLD == getSkinnable().getTimeFont()) { oneSecondSegment.setText("_"); } else if (LcdClock.LcdFont.ELEKTRA == getSkinnable().getTimeFont()) { oneSecondSegment.setText("_"); } // Add segments to background timeText backgroundTextBuilder.setLength(0); backgroundTextBuilder.append(oneTimeSegment.getText()); backgroundTextBuilder.append(oneTimeSegment.getText()); backgroundTextBuilder.append(":"); backgroundTextBuilder.append(oneTimeSegment.getText()); backgroundTextBuilder.append(oneTimeSegment.getText()); backgroundTimeText.setText(backgroundTextBuilder.toString()); backgroundSecondText.setText("88"); } private void updateLcd() { timeText.setText(ensureTwoDigits(getSkinnable().getTime().getHour()) + ":" + ensureTwoDigits(getSkinnable().getTime().getMinute())); secondText.setText(ensureTwoDigits(getSkinnable().getTime().getSecond())); updateBackgroundText(); // Visualize the lcd semitransparent background timeText backgroundTimeText.setX(width - 2 - backgroundTimeText.getLayoutBounds().getWidth() - valueOffsetRight); backgroundTimeText.setY(height - (backgroundTimeText.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5); timeText.setX((width - 2 - timeText.getLayoutBounds().getWidth()) - valueOffsetRight); // Update the title title.setText(getSkinnable().getTitle()); title.setX((width - title.getLayoutBounds().getWidth()) * 0.5); // Update the lower center timeText dayOfWeekText.setText(getSkinnable().getTime().getDayOfWeek().getDisplayName(TextStyle.FULL_STANDALONE, getSkinnable().getLocale())); dayOfWeekText.setX(0.0416666667 * height); // Update the lower right timeText dateText.setText(dateFormat.format(getSkinnable().getTime())); dateText.setX(width - dateText.getLayoutBounds().getWidth() - 0.0416666667 * height); dateText.setY(main.getLayoutY() + height - 3 - 0.0416666667 * height); } private void resize() { width = getSkinnable().getWidth(); height = getSkinnable().getHeight(); if (getSkinnable().isKeepAspect()) { if (aspectRatio * width > height) { width = 1 / (aspectRatio / height); } else if (1 / (aspectRatio / height) > width) { height = aspectRatio * width; } } if (width > 0 && height > 0) { main.setPrefSize(width, height); mainInnerShadow0.setRadius(3.0 / 132.0 * height); mainInnerShadow1.setRadius(2.0 / 132.0 * height); if (crystalOverlay.isVisible()) { crystalClip.setWidth(width); crystalClip.setHeight(height); crystalOverlay.setImage(createNoiseImage(width, height, DARK_NOISE_COLOR, BRIGHT_NOISE_COLOR, 8)); crystalOverlay.setCache(true); } alarm.setPrefSize(0.15 * height, 0.15 * height); alarm.setTranslateX(width - alarm.getPrefWidth() - height * 0.05); alarm.setTranslateY(height * 0.42 - alarm.getPrefHeight()); updateFonts(); // Setup the lcd unit secondText.setFont(secondFont); secondText.setTextOrigin(VPos.BASELINE); secondText.setTextAlignment(TextAlignment.RIGHT); secondText.setX((width - secondText.getLayoutBounds().getWidth()) - height * 0.04); secondText.setY(height - (timeText.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5); valueOffsetRight = (secondText.getLayoutBounds().getWidth() + height * 0.0833333333); // distance between value and unit timeText.setX(width - 2 - timeText.getLayoutBounds().getWidth() - valueOffsetRight); timeText.setY(height - (timeText.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5); // Visualize the lcd semitransparent background timeText updateBackgroundText(); backgroundTimeText.setX(width - 2 - backgroundTimeText.getLayoutBounds().getWidth() - valueOffsetRight); backgroundTimeText.setY(height - (backgroundTimeText.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5); backgroundSecondText.setX((width - secondText.getLayoutBounds().getWidth()) - height * 0.04); backgroundSecondText.setY(height - (timeText.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5); // Setup the font for the lcd title, number system, min measured, max measure and former value // Title title.setFont(titleFont); title.setTextOrigin(VPos.BASELINE); title.setTextAlignment(TextAlignment.CENTER); title.setText(getSkinnable().getTime().getDayOfWeek().name()); title.setX((width - title.getLayoutBounds().getWidth()) * 0.5); title.setY(main.getLayoutY() + title.getLayoutBounds().getHeight() - 0.04 * height + 2); // Date dateText.setFont(smallFont); dateText.setTextOrigin(VPos.BASELINE); dateText.setTextAlignment(TextAlignment.RIGHT); dateText.setText(getSkinnable().getTime().getMonthValue() + "/" + getSkinnable().getTime().getDayOfMonth() + "/" + getSkinnable().getTime().getYear()); dateText.setX(main.getLayoutX() + (width - dateText.getLayoutBounds().getWidth()) * 0.5); dateText.setY(main.getLayoutY() + height - 3 - 0.0416666667 * height); // Day of week dayOfWeekText.setFont(smallFont); dayOfWeekText.setTextOrigin(VPos.BASELINE); dayOfWeekText.setTextAlignment(TextAlignment.LEFT); dayOfWeekText.setX(0.0416666667 * height); dayOfWeekText.setY(main.getLayoutY() + height - 3 - 0.0416666667 * height); } } }