/* * SPDX-License-Identifier: Apache-2.0 * * Copyright 2016-2020 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 * * https://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.tilesfx.skins; import eu.hansolo.tilesfx.Tile; import eu.hansolo.tilesfx.Tile.ItemSortingTopic; import eu.hansolo.tilesfx.chart.ChartData; import eu.hansolo.tilesfx.events.ChartDataEvent; import eu.hansolo.tilesfx.events.ChartDataEvent.EventType; import eu.hansolo.tilesfx.events.ChartDataEventListener; import eu.hansolo.tilesfx.fonts.Fonts; import eu.hansolo.tilesfx.tools.Helper; import javafx.beans.DefaultProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectPropertyBase; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import java.time.Duration; import java.time.Instant; import java.util.Locale; import static eu.hansolo.tilesfx.tools.Helper.clamp; /** * User: hansolo * Date: 23.12.16 * Time: 13:10 */ @DefaultProperty("children") public class BarChartItem extends Region implements Comparable<BarChartItem>{ private static final double PREFERRED_WIDTH = 250; private static final double PREFERRED_HEIGHT = 30; private static final double MINIMUM_WIDTH = 25; private static final double MINIMUM_HEIGHT = 25; private static final double MAXIMUM_WIDTH = 1024; private static final double MAXIMUM_HEIGHT = 72; private static final double ASPECT_RATIO = PREFERRED_HEIGHT / PREFERRED_WIDTH; private double width; private double height; private double size; private double parentWidth; private double parentHeight; private Text nameText; private Text valueText; private Rectangle barBackground; private Rectangle bar; private Pane pane; private ObjectProperty<Color> nameColor; private ObjectProperty<Color> valueColor; private ObjectProperty<Color> barBackgroundColor; private String formatString; private Locale locale; private double maxValue; private double stepSize; private ChartData chartData; // ******************** Constructors ************************************** public BarChartItem() { this("", 0, Instant.now(), Duration.ZERO, Tile.BLUE); } public BarChartItem(final String NAME) { this(NAME, 0, Instant.now(), Duration.ZERO, Tile.BLUE); } public BarChartItem(final String NAME, final double VALUE) { this(NAME, VALUE, Instant.now(), Duration.ZERO, Tile.BLUE); } public BarChartItem(final String NAME, final Instant TIMESTAMP) { this(NAME, 0, TIMESTAMP, Duration.ZERO, Tile.BLUE); } public BarChartItem(final String NAME, final Duration DURATION) { this(NAME, 0, Instant.now(), DURATION, Tile.BLUE); } public BarChartItem(final String NAME, final double VALUE, final Color COLOR) { this(NAME, VALUE, Instant.now(), Duration.ZERO, COLOR); } public BarChartItem(final String NAME, final double VALUE, final Instant TIMESTAMP, final Color COLOR) { this(NAME, VALUE, TIMESTAMP, Duration.ZERO, COLOR); } public BarChartItem(final String NAME, final double VALUE, final Duration DURATION, final Color COLOR) { this(NAME, VALUE, Instant.now(), DURATION, COLOR); } public BarChartItem(final String NAME, final double VALUE, final Instant TIMESTAMP, final Duration DURATION, final Color COLOR) { nameColor = new ObjectPropertyBase<Color>(Tile.FOREGROUND) { @Override protected void invalidated() { nameText.setFill(get()); } @Override public Object getBean() { return BarChartItem.this; } @Override public String getName() { return "nameColor"; } }; valueColor = new ObjectPropertyBase<Color>(Tile.FOREGROUND) { @Override protected void invalidated() { valueText.setFill(get()); } @Override public Object getBean() { return BarChartItem.this; } @Override public String getName() { return "valueColor"; } }; barBackgroundColor = new ObjectPropertyBase<Color>(Color.rgb(72, 72, 72)) { @Override protected void invalidated() { barBackground.setFill(get()); } @Override public Object getBean() { return BarChartItem.this; } @Override public String getName() { return "barBackgroundColor"; } }; formatString = "%.0f"; locale = Locale.US; maxValue = 100; chartData = new ChartData(NAME, VALUE, TIMESTAMP, DURATION, COLOR); stepSize = PREFERRED_WIDTH * 0.85 / maxValue; parentWidth = 250; parentHeight = 250; initGraphics(); registerListeners(); } // ******************** Initialization ************************************ private void initGraphics() { if (Double.compare(getPrefWidth(), 0.0) <= 0 || Double.compare(getPrefHeight(), 0.0) <= 0 || Double.compare(getWidth(), 0.0) <= 0 || Double.compare(getHeight(), 0.0) <= 0) { if (getPrefWidth() > 0 && getPrefHeight() > 0) { setPrefSize(getPrefWidth(), getPrefHeight()); } else { setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT); } } nameText = new Text(getName()); nameText.setTextOrigin(VPos.TOP); valueText = new Text(String.format(locale, formatString, getValue())); valueText.setTextOrigin(VPos.TOP); barBackground = new Rectangle(); bar = new Rectangle(); pane = new Pane(nameText, valueText, barBackground, bar); pane.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY))); getChildren().setAll(pane); } private void registerListeners() { widthProperty().addListener(o -> resize()); heightProperty().addListener(o -> resize()); } // ******************** Methods ******************************************* @Override protected double computeMinWidth(final double HEIGHT) { return MINIMUM_WIDTH; } @Override protected double computeMinHeight(final double WIDTH) { return MINIMUM_HEIGHT; } @Override protected double computePrefWidth(final double HEIGHT) { return super.computePrefWidth(HEIGHT); } @Override protected double computePrefHeight(final double WIDTH) { return super.computePrefHeight(WIDTH); } @Override protected double computeMaxWidth(final double HEIGHT) { return MAXIMUM_WIDTH; } @Override protected double computeMaxHeight(final double WIDTH) { return MAXIMUM_HEIGHT; } @Override public ObservableList<Node> getChildren() { return super.getChildren(); } public String getName() { return chartData.getName(); } public void setName(final String NAME) { chartData.setName(NAME); nameText.setText(NAME); } public double getValue() { return chartData.getValue(); } public void setValue(final double VALUE) { chartData.setValue(VALUE); } public Instant getTimestamp() { return chartData.getTimestamp(); } public void setTimestamp(final Instant TIMESTAMP) { chartData.setTimestamp(TIMESTAMP); } public Duration getDuration() { return chartData.getDuration(); } public void setDuration(final Duration DURATION) { chartData.setDuration(DURATION); } public Color getNameColor() { return nameColor.get(); } public void setNameColor(final Color COLOR) { nameColor.set(COLOR); } public ObjectProperty<Color> nameColorProperty() { return nameColor; } public Color getValueColor() { return valueColor.get(); } public void setValueColor(final Color COLOR) { valueColor.set(COLOR); } public ObjectProperty<Color> valueColorProperty() { return valueColor; } public ChartData getChartData() { return chartData; } public void setChartData(final ChartData DATA) { chartData = DATA; chartData.fireChartDataEvent(new ChartDataEvent(EventType.UPDATE, chartData)); } public Color getBarBackgroundColor() { return barBackgroundColor.get(); } public void setBarBackgroundColor(final Color COLOR) { barBackgroundColor.set(COLOR); } public ObjectProperty<Color> barBackgroundColorProperty() { return barBackgroundColor; } public Color getBarColor() { return chartData.getFillColor(); } public void setBarColor(final Color COLOR) { chartData.setFillColor(COLOR); } @Override public int compareTo(final BarChartItem ITEM) { return Double.compare(getValue(), ITEM.getValue()); } public void setStepSize(final double STEP_SIZE) { stepSize = STEP_SIZE; updateBar(getValue()); } public void setMaxValue(final double MAX_VALUE) { maxValue = MAX_VALUE; stepSize = (parentWidth - size * 0.15) / maxValue; updateBar(getValue()); } public void setLocale(final Locale LOCALE) { locale = LOCALE; valueText.setText(String.format(locale, formatString, getValue())); } public String getFormatString() { return formatString; } public void setFormatString(final String FORMAT_STRING) { formatString = FORMAT_STRING; valueText.setText(String.format(locale, formatString, getValue())); } protected void setParentSize(final double WIDTH, final double HEIGHT) { parentWidth = WIDTH; parentHeight = HEIGHT; resize(); } private void updateBar(final double VALUE) { valueText.setText(String.format(locale, formatString, VALUE)); valueText.relocate((parentWidth - size * 0.05) - valueText.getLayoutBounds().getWidth(), 0); bar.setWidth(clamp(0, (parentWidth - size * 0.15), VALUE * stepSize)); bar.setFill(getBarColor()); } // ******************** Event Handling ************************************ public void setOnChartDataEvent(final ChartDataEventListener LISTENER) { chartData.addChartDataEventListener(LISTENER); } public void addChartDataEventListener(final ChartDataEventListener LISTENER) { chartData.addChartDataEventListener(LISTENER); } public void removeChartDataEventListener(final ChartDataEventListener LISTENER) { chartData.removeChartDataEventListener(LISTENER); } // ******************** Resizing ****************************************** private void resize() { width = getWidth() - getInsets().getLeft() - getInsets().getRight(); height = getHeight() - getInsets().getTop() - getInsets().getBottom(); size = parentWidth < parentHeight ? parentWidth : parentHeight; if (ASPECT_RATIO * width > height) { width = 1 / (ASPECT_RATIO / height); } else if (1 / (ASPECT_RATIO / height) > width) { height = ASPECT_RATIO * width; } if (width > 0 && height > 0) { stepSize = (parentWidth - size * 0.15) / maxValue; double itemHeight = Helper.clamp(MINIMUM_HEIGHT, MAXIMUM_HEIGHT, height * 0.14); pane.setMinSize(parentWidth, itemHeight); pane.setMaxSize(parentWidth, itemHeight); pane.setPrefSize(parentWidth, itemHeight); double fontSize = Helper.clamp(12, MAXIMUM_HEIGHT * 0.5, size * 0.06); nameText.setFont(Fonts.latoRegular(fontSize)); nameText.setX(size * 0.05); nameText.setY(0); valueText.setFont(Fonts.latoRegular(fontSize)); valueText.relocate((parentWidth - size * 0.05) - valueText.getLayoutBounds().getWidth(), 0); barBackground.setX(size * 0.075); barBackground.setY(fontSize * 1.5); barBackground.setWidth(parentWidth - size * 0.15); barBackground.setHeight(size * 0.01); barBackground.setHeight(fontSize / 6); bar.setX(size * 0.075); bar.setY(fontSize * 1.35); bar.setWidth(clamp(0, (parentWidth - size * 0.15), getValue() * stepSize)); bar.setHeight(fontSize / 2); redraw(); } } private void redraw() { nameText.setFill(getNameColor()); valueText.setFill(getValueColor()); barBackground.setFill(getBarBackgroundColor()); bar.setFill(getBarColor()); } @Override public String toString() { return new StringBuilder(getName()).append(",").append(getValue()).append(",").append(getBarColor()).toString(); } }