/*
* 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.common.ValueEvent;
import eu.hansolo.enzo.lcd.Lcd;
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.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Random;
public class LcdSkin extends SkinBase<Lcd> implements Skin<Lcd> {
private static final double PREFERRED_WIDTH = 132;
private static final double PREFERRED_HEIGHT = 48;
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 double aspectRatio = PREFERRED_HEIGHT / PREFERRED_WIDTH;
private static Text oneSegment = new Text("8");
private static Text dotSegment = new Text(".");
private static final DecimalFormat DEC_FORMAT = new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.US));
private static final boolean SCIFI_FORMAT = false;
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 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 threshold;
private Region trendDown;
private Region trendFalling;
private Region trendSteady;
private Region trendRising;
private Region trendUp;
private Region battery;
private Region signal;
private Region alarm;
private Text text;
private Text backgroundText;
private Text unitText;
private Text title;
private Text lowerRightText;
private Text upperLeftText;
private Text upperRightText;
private Text lowerCenterText;
private double valueOffsetLeft;
private double valueOffsetRight;
private double digitalFontSizeFactor;
private Font valueFont;
private Font unitFont;
private Font titleFont;
private Font smallFont;
private double oneSegmentWidth;
private double dotSegmentWidth;
private double widthOfDecimals;
private double availableWidth;
private int noOfSegments;
private StringBuilder backgroundTextBuilder;
private StringBuilder decBuffer;
private Group shadowGroup;
// ******************** Constructors **************************************
public LcdSkin(final Lcd CONTROL) {
super(CONTROL);
valueOffsetLeft = 0.0;
valueOffsetRight = 0.0;
digitalFontSizeFactor = 1.0;
backgroundTextBuilder = new StringBuilder();
decBuffer = new StringBuilder(16);
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);
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);
}
if (getSkinnable().getPrefWidth() != PREFERRED_WIDTH || getSkinnable().getPrefHeight() != PREFERRED_HEIGHT) {
aspectRatio = getSkinnable().getPrefHeight() / getSkinnable().getPrefWidth();
}
}
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, width, 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(crystalClip);
crystalOverlay.setOpacity(getSkinnable().isCrystalOverlayVisible() ? 1 : 0);
threshold = new Region();
threshold.getStyleClass().setAll("threshold");
threshold.setOpacity(0);
trendDown = new Region();
trendDown.getStyleClass().setAll("trend-down");
trendDown.setOpacity((getSkinnable().isTrendVisible() && Lcd.Trend.DOWN == getSkinnable().getTrend()) ? 1 : 0);
trendFalling = new Region();
trendFalling.getStyleClass().setAll("trend-falling");
trendFalling.setOpacity((getSkinnable().isTrendVisible() && Lcd.Trend.FALLING == getSkinnable().getTrend()) ? 1 : 0);
trendSteady = new Region();
trendSteady.getStyleClass().setAll("trend-steady");
trendSteady.setOpacity((getSkinnable().isTrendVisible() && Lcd.Trend.STEADY == getSkinnable().getTrend()) ? 1 : 0);
trendRising = new Region();
trendRising.getStyleClass().setAll("trend-rising");
trendRising.setOpacity((getSkinnable().isTrendVisible() && Lcd.Trend.RISING == getSkinnable().getTrend()) ? 1 : 0);
trendUp = new Region();
trendUp.getStyleClass().setAll("trend-up");
trendUp.setOpacity((getSkinnable().isTrendVisible() && Lcd.Trend.UP == getSkinnable().getTrend()) ? 1 : 0);
battery = new Region();
battery.getStyleClass().setAll("battery-empty");
battery.setOpacity(getSkinnable().isBatteryVisible() ? 1 : 0);
signal = new Region();
signal.getStyleClass().setAll("signal");
signal.setOpacity(getSkinnable().isSignalVisible() ? 1 : 0);
alarm = new Region();
alarm.getStyleClass().setAll("alarm");
alarm.setOpacity(getSkinnable().isAlarmVisible() ? 1 : 0);
backgroundText = new Text(getSkinnable().isTextMode() ? getSkinnable().getText() : Double.toString(getSkinnable().getValue()));
backgroundText.getStyleClass().setAll("fg-trsp");
backgroundText.setOpacity((Lcd.LcdFont.LCD == getSkinnable().getValueFont() || Lcd.LcdFont.ELEKTRA == getSkinnable().getValueFont()) ? 1 : 0);
text = new Text(getSkinnable().isTextMode() ? getSkinnable().getText() : Double.toString(getSkinnable().getValue()));
text.getStyleClass().setAll("fg");
unitText = new Text(getSkinnable().getUnit());
unitText.getStyleClass().setAll("fg");
unitText.setOpacity(getSkinnable().isUnitVisible() ? 1 : 0);
title = new Text(getSkinnable().getTitle());
title.getStyleClass().setAll("fg");
title.setOpacity(getSkinnable().isTitleVisible() ? 1 : 0);
lowerRightText = getSkinnable().isNumberSystemVisible() ? new Text(getSkinnable().getNumberSystem().toString()) : new Text(getSkinnable().getLowerRightText());
lowerRightText.getStyleClass().setAll("fg");
lowerRightText.setOpacity(getSkinnable().isLowerRightTextVisible() ? 1 : 0);
upperLeftText = getSkinnable().isMinMeasuredValueVisible() ? new Text(Double.toString(getSkinnable().getMaxValue())) : new Text(getSkinnable().getUpperLeftText());
upperLeftText.getStyleClass().setAll("fg");
upperLeftText.setOpacity(getSkinnable().isMinMeasuredValueVisible() ? 1 : 0);
upperRightText = getSkinnable().isMaxMeasuredValueVisible() ? new Text(Double.toString(getSkinnable().getMinValue())) : new Text(getSkinnable().getUpperRightText());
upperRightText.getStyleClass().setAll("fg");
upperRightText.setOpacity(getSkinnable().isMaxMeasuredValueVisible() ? 1 : 0);
lowerCenterText = new Text(getSkinnable().isFormerValueVisible() ? Double.toString(getSkinnable().getFormerValue()) : getSkinnable().getLowerCenterText());
lowerCenterText.getStyleClass().setAll("fg");
shadowGroup = new Group();
shadowGroup.setEffect(getSkinnable().isForegroundShadowVisible() ? FOREGROUND_SHADOW : null);
shadowGroup.getChildren().setAll(threshold,
trendDown,
trendFalling,
trendSteady,
trendRising,
trendUp,
battery,
signal,
alarm,
text,
unitText,
title,
lowerRightText,
upperLeftText,
upperRightText,
lowerCenterText);
pane = new Pane();
pane.getChildren().setAll(main,
crystalOverlay,
backgroundText,
shadowGroup);
getChildren().setAll(pane);
resize();
updateLcd();
}
private void registerListeners() {
getSkinnable().widthProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") );
getSkinnable().heightProperty().addListener(observable -> handleControlPropertyChanged("RESIZE") );
getSkinnable().titleProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().unitProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().lowerRightTextProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().numberSystemProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().textModeProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().textProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().currentValueProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().lowerCenterTextProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().minMeasuredValueProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().maxMeasuredValueProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().upperLeftTextProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().upperRightTextProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().batteryChargeProperty().addListener(observable -> handleControlPropertyChanged("UPDATE") );
getSkinnable().signalStrengthProperty().addListener(observable -> handleControlPropertyChanged("UPDATE"));
getSkinnable().prefWidthProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE") );
getSkinnable().prefHeightProperty().addListener(observable -> handleControlPropertyChanged("PREF_SIZE") );
getSkinnable().valueFontProperty().addListener(observable -> handleControlPropertyChanged("FONT") );
getSkinnable().smallFontProperty().addListener(observable -> handleControlPropertyChanged("FONT") );
getSkinnable().unitFontProperty().addListener(observable -> handleControlPropertyChanged("FONT") );
getSkinnable().numberSystemVisibleProperty().addListener(observable -> handleControlPropertyChanged("NUMBER_SYSTEM_VISIBLE") );
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().animationDurationProperty().addListener(observable -> handleControlPropertyChanged("ANIMATION_DURATION") );
getSkinnable().trendProperty().addListener(observable -> handleControlPropertyChanged("TREND") );
getSkinnable().valueVisibleProperty().addListener(observable -> handleControlPropertyChanged("VALUE_VISIBLE") );
getSkinnable().unitVisibleProperty().addListener(observable -> handleControlPropertyChanged("UNIT_VISIBLE") );
getSkinnable().lowerCenterTextVisibleProperty().addListener(observable -> handleControlPropertyChanged("LOWER_CENTER_VISIBLE") );
getSkinnable().lowerRightTextVisibleProperty().addListener(observable -> handleControlPropertyChanged("LOWER_RIGHT_VISIBLE") );
getSkinnable().upperLeftTextVisibleProperty().addListener(observable -> handleControlPropertyChanged("UPPER_LEFT_VISIBLE") );
getSkinnable().upperRightTextVisibleProperty().addListener(observable -> handleControlPropertyChanged("UPPER_RIGHT_VISIBLE") );
getSkinnable().batteryVisibleProperty().addListener(observable -> handleControlPropertyChanged("BATTERY_VISIBLE") );
getSkinnable().signalVisibleProperty().addListener(observable -> handleControlPropertyChanged("SIGNAL_VISIBLE"));
getSkinnable().alarmVisibleProperty().addListener(observable -> handleControlPropertyChanged("ALARM_VISIBLE") );
getSkinnable().formerValueVisibleProperty().addListener(observable -> handleControlPropertyChanged("FORMER_VALUE_VISIBLE") );
getSkinnable().maxMeasuredValueVisibleProperty().addListener(observable -> handleControlPropertyChanged("MAX_MEASURED_VISIBLE") );
getSkinnable().minMeasuredValueVisibleProperty().addListener(observable -> handleControlPropertyChanged("MIN_MEASURED_VISIBLE") );
getSkinnable().addEventFilter(ValueEvent.VALUE_EXCEEDED, valueEvent -> {handleControlPropertyChanged("THRESHOLD_EXCEEDED");});
getSkinnable().addEventFilter(ValueEvent.VALUE_UNDERRUN, valueEvent -> {handleControlPropertyChanged("THRESHOLD_UNDERRUN");});
getSkinnable().getStyleClass().addListener(new ListChangeListener<String>() {
@Override public void onChanged(Change<? extends 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 ("TREND".equals(PROPERTY)) {
updateTrend();
} else if ("THRESHOLD_EXCEEDED".equals(PROPERTY)) {
if (getSkinnable().isThresholdVisible()) {
threshold.setOpacity(getSkinnable().isThresholdBehaviorInverted() ? 0 : 1);
}
} else if ("THRESHOLD_UNDERRUN".equals(PROPERTY)) {
if (getSkinnable().isThresholdVisible()) {
threshold.setOpacity(getSkinnable().isThresholdBehaviorInverted() ? 1 : 0);
}
} else if ("FONT".equals(PROPERTY)) {
updateFonts();
} else if ("VALUE_VISIBLE".equals(PROPERTY)) {
text.setOpacity(getSkinnable().isValueVisible() ? 1 : 0);
} else if ("UNIT_VISIBLE".equals(PROPERTY)) {
unitText.setOpacity(getSkinnable().isUnitVisible() ? 1 : 0);
} else if ("FORMER_VALUE_VISIBLE".equals(PROPERTY)) {
lowerCenterText.setOpacity(getSkinnable().isFormerValueVisible() ? 1 : 0);
} else if ("MAX_MEASURED_VISIBLE".equals(PROPERTY)) {
upperRightText.setOpacity(getSkinnable().isMaxMeasuredValueVisible() ? 1 : 0);
} else if ("MIN_MEASURED_VISIBLE".equals(PROPERTY)) {
upperLeftText.setOpacity(getSkinnable().isMinMeasuredValueVisible() ? 1 : 0);
} else if ("NUMBER_SYSTEM_VISIBLE".equals(PROPERTY)) {
updateLcd();
} else if ("LOWER_RIGHT_VISIBLE".equals(PROPERTY)) {
lowerRightText.setOpacity(getSkinnable().isLowerRightTextVisible() ? 1 : 0);
} else if ("UPPER_LEFT_VISIBLE".equals(PROPERTY)) {
upperLeftText.setOpacity(getSkinnable().isUpperLeftTextVisible() ? 1 : 0);
} else if ("UPPER_RIGHT_VISIBLE".equals(PROPERTY)) {
upperRightText.setOpacity(getSkinnable().isUpperRightTextVisible() ? 1 : 0);
} else if ("BATTERY_VISIBLE".equals(PROPERTY)) {
battery.setOpacity(getSkinnable().isBatteryVisible() ? 1 : 0);
} else if ("SIGNAL_VISIBLE".equals(PROPERTY)) {
signal.setOpacity(getSkinnable().isSignalVisible() ? 1 : 0);
} else if ("ALARM_VISIBLE".equals(PROPERTY)) {
alarm.setOpacity(getSkinnable().isAlarmVisible() ? 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);
}
// ******************** Private Methods ***********************************
private boolean isNoOfDigitsInvalid() {
final double AVAILABLE_WIDTH = width - 2 - valueOffsetLeft - valueOffsetRight;
final double NEEDED_WIDTH = text.getLayoutBounds().getWidth();
return Double.compare(AVAILABLE_WIDTH, NEEDED_WIDTH) < 0;
}
private String formatLcdValue(final double VALUE, final int DECIMALS) {
decBuffer.setLength(0);
decBuffer.append("0");
if (DECIMALS > 0) {
decBuffer.append(".");
}
for (int i = 0; i < DECIMALS; i++) {
decBuffer.append("0");
}
if (SCIFI_FORMAT) {
decBuffer.append("E0");
}
decBuffer.trimToSize();
DEC_FORMAT.applyPattern(decBuffer.toString());
//decBuffer.setLength(0);
//decBuffer.append("%.").append(DECIMALS).append("f");
//return String.format(Locale.US, decBuffer.toString(), VALUE);
return DEC_FORMAT.format(VALUE);
}
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().getValueFont()) {
case LCD:
valueFont = Font.font("Digital-7", (0.6 * height));
digitalFontSizeFactor = 1.4;
break;
case DIGITAL:
valueFont = Font.font("Digital Readout Upright", (0.5833333333 * height));
break;
case DIGITAL_BOLD:
valueFont = Font.font("Digital Readout Thick Upright", (0.5833333333 * height));
break;
case ELEKTRA:
valueFont = Font.font("Elektra", (0.62 * height));
break;
case STANDARD:
default:
valueFont = Font.font("Open Sans", FontWeight.NORMAL, (0.5 * height));
break;
}
backgroundText.setFont(valueFont);
backgroundText.setOpacity((Lcd.LcdFont.LCD == getSkinnable().getValueFont() ||
Lcd.LcdFont.DIGITAL == getSkinnable().getValueFont() ||
Lcd.LcdFont.DIGITAL_BOLD == getSkinnable().getValueFont() ||
Lcd.LcdFont.ELEKTRA == getSkinnable().getValueFont()) &&
!getSkinnable().isTextMode() ? 1 : 0);
text.setFont(valueFont);
unitFont = Font.font(getSkinnable().getUnitFont(), FontWeight.NORMAL, (0.26 * height));
titleFont = Font.font(getSkinnable().getTitleFont(), FontWeight.BOLD, (0.1666666667 * height));
smallFont = Font.font(getSkinnable().getSmallFont(), FontWeight.NORMAL, (0.1666666667 * height));
}
private void updateTrend() {
if (getSkinnable().isTrendVisible()) {
switch (getSkinnable().getTrend()) {
case UP:
trendUp.setOpacity(1);
trendRising.setOpacity(0);
trendSteady.setOpacity(0);
trendFalling.setOpacity(0);
trendDown.setOpacity(0);
break;
case RISING:
trendUp.setOpacity(0);
trendRising.setOpacity(1);
trendSteady.setOpacity(0);
trendFalling.setOpacity(0);
trendDown.setOpacity(0);
break;
case STEADY:
trendUp.setOpacity(0);
trendRising.setOpacity(0);
trendSteady.setOpacity(1);
trendFalling.setOpacity(0);
trendDown.setOpacity(0);
break;
case FALLING:
trendUp.setOpacity(0);
trendRising.setOpacity(0);
trendSteady.setOpacity(0);
trendFalling.setOpacity(1);
trendDown.setOpacity(0);
break;
case DOWN:
trendUp.setOpacity(0);
trendRising.setOpacity(0);
trendSteady.setOpacity(0);
trendFalling.setOpacity(0);
trendDown.setOpacity(1);
break;
default:
trendUp.setOpacity(0);
trendRising.setOpacity(0);
trendSteady.setOpacity(0);
trendFalling.setOpacity(0);
trendDown.setOpacity(0);
break;
}
}
}
private void updateBackgroundText() {
// Setup the semitransparent background text
backgroundText.setTextOrigin(VPos.BASELINE);
backgroundText.setTextAlignment(TextAlignment.RIGHT);
// Setup the semitransparent background text
// Width of one segment
oneSegment.setFont(valueFont);
dotSegment.setText(".");
if (Lcd.LcdFont.LCD == getSkinnable().getValueFont()) {
oneSegment.setText("8");
} else if (Lcd.LcdFont.DIGITAL == getSkinnable().getValueFont()) {
oneSegment.setText("_");
} else if (Lcd.LcdFont.DIGITAL_BOLD == getSkinnable().getValueFont()) {
oneSegment.setText("_");
} else if (Lcd.LcdFont.ELEKTRA == getSkinnable().getValueFont()) {
oneSegment.setText("_");
}
oneSegmentWidth = oneSegment.getLayoutBounds().getWidth();
dotSegmentWidth = dotSegment.getLayoutBounds().getWidth();
if (Lcd.NumberSystem.DECIMAL == getSkinnable().getNumberSystem()) {
// Width of decimals
widthOfDecimals = 0 == getSkinnable().getDecimals() ? 0 : getSkinnable().getDecimals() * oneSegmentWidth + (Lcd.LcdFont.LCD == getSkinnable().getValueFont() ? oneSegmentWidth : dotSegmentWidth);
// Available width
availableWidth = width - (0.0151515152 * width) - (0.0416666667 * height) - 2 - valueOffsetRight - widthOfDecimals;
// Number of segments
noOfSegments = (int) Math.floor(availableWidth / oneSegmentWidth);
// Add segments to background text
backgroundTextBuilder.setLength(0);
for (int i = 0 ; i < getSkinnable().getDecimals() ; i++) {
backgroundTextBuilder.append(oneSegment.getText());
}
if (getSkinnable().getDecimals() != 0) {
backgroundTextBuilder.insert(0, ".");
}
for (int i = 0 ; i < noOfSegments ; i++) {
backgroundTextBuilder.insert(0, oneSegment.getText());
}
backgroundText.setText(backgroundTextBuilder.toString());
} else {
// Available width
availableWidth = width - (0.0151515152 * width) - (0.0416666667 * height) - 2 - valueOffsetRight;
// Number of segments
noOfSegments = (int) Math.floor(availableWidth / oneSegmentWidth);
// Add segments to background text
backgroundTextBuilder.setLength(0);
for (int i = 0 ; i < noOfSegments ; i++) {
backgroundTextBuilder.insert(0, oneSegment.getText());
}
backgroundText.setText(backgroundTextBuilder.toString());
}
}
private void updateLcd() {
if (getSkinnable().isTextMode() && Lcd.LcdFont.LCD != getSkinnable().getValueFont()) {
text.setText(getSkinnable().getText());
} else {
switch (getSkinnable().getNumberSystem()) {
case HEXADECIMAL:
text.setText(Integer.toHexString((int) getSkinnable().getCurrentValue()).toUpperCase());
break;
case OCTAL:
text.setText(Integer.toOctalString((int) getSkinnable().getCurrentValue()).toUpperCase());
break;
case DECIMAL:
default:
text.setText(formatLcdValue(getSkinnable().getCurrentValue(), getSkinnable().getDecimals()));
break;
}
}
if (isNoOfDigitsInvalid()) {
text.setText("-E-");
}
updateBackgroundText();
// Visualize the lcd semitransparent background text
if (getSkinnable().isUnitVisible()) {
backgroundText.setX(width - 2 - backgroundText.getLayoutBounds().getWidth() - valueOffsetRight);
} else {
backgroundText.setX((width - backgroundText.getLayoutBounds().getWidth()) - valueOffsetRight);
}
backgroundText.setY(height - (backgroundText.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5);
if (getSkinnable().isUnitVisible()) {
text.setX((width - 2 - text.getLayoutBounds().getWidth()) - valueOffsetRight);
} else {
text.setX((width - text.getLayoutBounds().getWidth()) - valueOffsetRight);
}
// Update the title
title.setText(getSkinnable().getTitle());
title.setX((width - title.getLayoutBounds().getWidth()) * 0.5);
// Update the upper left text
upperLeftText.setText(getSkinnable().isMinMeasuredValueVisible() ? formatLcdValue(getSkinnable().getMinMeasuredValue(), getSkinnable().getMinMeasuredValueDecimals()) : getSkinnable().getUpperLeftText());
if (upperLeftText.getX() + upperLeftText.getLayoutBounds().getWidth() > title.getX()) {
upperLeftText.setText("...");
}
// Update the upper right text
upperRightText.setText(getSkinnable().isMaxMeasuredValueVisible() ? formatLcdValue(getSkinnable().getMaxMeasuredValue(), getSkinnable().getMaxMeasuredValueDecimals()) : getSkinnable().getUpperRightText());
upperRightText.setX(width - upperRightText.getLayoutBounds().getWidth() - 0.0416666667 * height);
if (upperRightText.getX() < title.getX() + title.getLayoutBounds().getWidth()) {
upperRightText.setText("...");
upperRightText.setX(width - upperRightText.getLayoutBounds().getWidth() - 0.0416666667 * height);
}
// Update the lower center text
lowerCenterText.setText(getSkinnable().isFormerValueVisible() ? formatLcdValue(getSkinnable().getFormerValue(), getSkinnable().getDecimals()) : getSkinnable().getLowerCenterText());
lowerCenterText.setX((width - lowerCenterText.getLayoutBounds().getWidth()) * 0.5);
// Update the lower right text
lowerRightText.setText(getSkinnable().isNumberSystemVisible() ? getSkinnable().getNumberSystem().toString() : getSkinnable().getLowerRightText());
lowerRightText.setX(width - lowerRightText.getLayoutBounds().getWidth() - 0.0416666667 * height);
lowerRightText.setY(main.getLayoutY() + height - 3 - 0.0416666667 * height);
if (lowerRightText.getX() < lowerCenterText.getX() + lowerCenterText.getLayoutBounds().getWidth()) {
lowerRightText.setText("...");
lowerRightText.setX(width - lowerRightText.getLayoutBounds().getWidth() - 0.0416666667 * height);
}
// Update battery charge
if (getSkinnable().getBatteryCharge() < 0.01) {
battery.getStyleClass().setAll("battery-empty");
} else if (getSkinnable().getBatteryCharge() < 0.06) {
battery.getStyleClass().setAll("battery-almost-empty");
} else if (getSkinnable().getBatteryCharge() < 0.26) {
battery.getStyleClass().setAll("battery-25");
} else if (getSkinnable().getBatteryCharge() < 0.51) {
battery.getStyleClass().setAll("battery-50");
} else if (getSkinnable().getBatteryCharge() < 0.76) {
battery.getStyleClass().setAll("battery-75");
} else if (getSkinnable().getBatteryCharge() < 0.96) {
battery.getStyleClass().setAll("battery-almost-full");
} else {
battery.getStyleClass().setAll("battery-full");
}
// Update signal strength
if (getSkinnable().getSignalStrength() < 0.06) {
signal.getStyleClass().setAll("signal", "signal-0");
} else if (getSkinnable().getSignalStrength() < 0.26) {
signal.getStyleClass().setAll("signal", "signal-25");
} else if (getSkinnable().getSignalStrength() < 0.51) {
signal.getStyleClass().setAll("signal", "signal-50");
} else if (getSkinnable().getSignalStrength() < 0.85) {
signal.getStyleClass().setAll("signal", "signal-75");
} else {
signal.getStyleClass().setAll("signal", "signal-100");
}
}
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);
}
threshold.setPrefSize(0.20 * height, 0.20 * height);
threshold.setTranslateX(0.027961994662429348 * width);
threshold.setTranslateY(0.75 * height - 2);
trendDown.setPrefSize(0.06718573425755356 * width, 0.1333622932434082 * height);
trendDown.setTranslateX(0.1439393939 * width);
trendDown.setTranslateY(0.8125 * height - 2);
trendFalling.setPrefSize(0.06982171896732214 * width, 0.13879903157552084 * height);
trendFalling.setTranslateX(0.1439393939 * width);
trendFalling.setTranslateY(0.8061291376749674 * height - 2);
trendSteady.setPrefSize(0.0676060878869259 * width, 0.1342292626698812 * height);
trendSteady.setTranslateX(0.1439393939 * width);
trendSteady.setTranslateY(0.8078853289286295 * height - 2);
trendRising.setPrefSize(0.06982171896732214 * width, 0.13879903157552084 * height);
trendRising.setTranslateX(0.1439393939 * width);
trendRising.setTranslateY(0.8050718307495117 * height - 2);
trendUp.setPrefSize(0.06718573425755356 * width, 0.1333622932434082 * height);
trendUp.setTranslateX(0.1439393939 * width);
trendUp.setTranslateY(0.8041377067565918 * height - 2);
battery.setPrefSize(0.0833333333 * width, 0.1458333333 * height);
battery.setTranslateX(0.6439393939 * width);
battery.setTranslateY(0.81 * height - 2);
signal.setPrefSize(0.0416666667 * height, 0.5 * height);
signal.setTranslateX(0.0151515152 * width);
signal.setTranslateY(0.25 * height);
alarm.setPrefSize(0.1666666667 * height, 0.1666666667 * height);
alarm.setTranslateX(0.2651515152 * width);
alarm.setTranslateY(0.7916666667 * height - 2);
updateFonts();
// Setup the lcd unit
unitText.setFont(unitFont);
unitText.setTextOrigin(VPos.BASELINE);
unitText.setTextAlignment(TextAlignment.RIGHT);
unitText.setText(getSkinnable().getUnit());
if (unitText.visibleProperty().isBound()) {
unitText.visibleProperty().unbind();
}
unitText.visibleProperty().bind(getSkinnable().unitVisibleProperty());
valueOffsetLeft = height * 0.04;
if (getSkinnable().isUnitVisible()) {
unitText.setX((width - unitText.getLayoutBounds().getWidth()) - height * 0.04);
unitText.setY(height - (text.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5);
valueOffsetRight = (unitText.getLayoutBounds().getWidth() + height * 0.0833333333); // distance between value and unit
text.setX(width - 2 - text.getLayoutBounds().getWidth() - valueOffsetRight);
} else {
valueOffsetRight = height * 0.0833333333;
text.setX((width - text.getLayoutBounds().getWidth()) - valueOffsetRight);
}
text.setY(height - (text.getLayoutBounds().getHeight() * digitalFontSizeFactor) * 0.5);
// Visualize the lcd semitransparent background text
updateBackgroundText();
if (getSkinnable().isUnitVisible()) {
backgroundText.setX(width - 2 - backgroundText.getLayoutBounds().getWidth() - valueOffsetRight);
} else {
backgroundText.setX((width - backgroundText.getLayoutBounds().getWidth()) - valueOffsetRight);
}
backgroundText.setY(height - (backgroundText.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().getTitle());
title.setX((width - title.getLayoutBounds().getWidth()) * 0.5);
title.setY(main.getLayoutY() + title.getLayoutBounds().getHeight() - 0.04 * height + 2);
// Info Text
lowerRightText.setFont(smallFont);
lowerRightText.setTextOrigin(VPos.BASELINE);
lowerRightText.setTextAlignment(TextAlignment.RIGHT);
lowerRightText.setText(getSkinnable().getNumberSystem().toString());
lowerRightText.setX(main.getLayoutX() + (main.getLayoutBounds().getWidth() - lowerRightText.getLayoutBounds().getWidth()) * 0.5);
lowerRightText.setY(main.getLayoutY() + height - 3 - 0.0416666667 * height);
// Min measured value
upperLeftText.setFont(smallFont);
upperLeftText.setTextOrigin(VPos.BASELINE);
upperLeftText.setTextAlignment(TextAlignment.RIGHT);
upperLeftText.setX(main.getLayoutX() + 0.0416666667 * height);
upperLeftText.setY(main.getLayoutY() + upperLeftText.getLayoutBounds().getHeight() - 0.04 * height + 2);
// Max measured value
upperRightText.setFont(smallFont);
upperRightText.setTextOrigin(VPos.BASELINE);
upperRightText.setTextAlignment(TextAlignment.RIGHT);
upperRightText.setY(main.getLayoutY() + upperRightText.getLayoutBounds().getHeight() - 0.04 * height + 2);
// Former value
lowerCenterText.setFont(smallFont);
lowerCenterText.setTextOrigin(VPos.BASELINE);
lowerCenterText.setTextAlignment(TextAlignment.CENTER);
lowerCenterText.setX((width - lowerCenterText.getLayoutBounds().getWidth()) * 0.5);
lowerCenterText.setY(main.getLayoutY() + height - 3 - 0.0416666667 * height);
}
}
}