Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
eu.hansolo.tilesfx.skins.DonutChartTileSkin Maven / Gradle / Ivy
/*
* Copyright (c) 2017 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.tilesfx.skins;
import eu.hansolo.tilesfx.Tile;
import eu.hansolo.tilesfx.events.ChartDataEventListener;
import eu.hansolo.tilesfx.events.TileEvent;
import eu.hansolo.tilesfx.events.TileEvent.EventType;
import eu.hansolo.tilesfx.fonts.Fonts;
import eu.hansolo.tilesfx.chart.ChartData;
import eu.hansolo.tilesfx.tools.Helper;
import javafx.collections.ListChangeListener;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.VPos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
/**
* Created by hansolo on 27.02.17.
*/
public class DonutChartTileSkin extends TileSkin {
private Text titleText;
private Text text;
private Canvas chartCanvas;
private GraphicsContext chartCtx;
private Canvas legendCanvas;
private GraphicsContext legendCtx;
private ListChangeListener chartDataListener;
private ChartDataEventListener chartEventListener;
private double centerX;
private double centerY;
private double innerRadius;
private double outerRadius;
private Tooltip selectionTooltip;
private EventHandler clickHandler;
private EventHandler moveHandler;
// ******************** Constructors **************************************
public DonutChartTileSkin(final Tile TILE) {
super(TILE);
}
// ******************** Initialization ************************************
@Override protected void initGraphics() {
super.initGraphics();
chartEventListener = e -> redraw();
tile.getChartData().forEach(chartData -> chartData.addChartDataEventListener(chartEventListener));
chartDataListener = c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().forEach(addedItem -> addedItem.addChartDataEventListener(chartEventListener));
} else if (c.wasRemoved()) {
c.getRemoved().forEach(removedItem -> removedItem.removeChartDataEventListener(chartEventListener));
}
}
drawChart();
drawLegend();
};
clickHandler = e -> {
double x = e.getX();
double y = e.getY();
double startAngle = 90;
double angle = 0;
List dataList = tile.isSortedData() ? tile.getChartData().stream().sorted(Comparator.comparingDouble(ChartData::getValue)).collect(Collectors.toList()) : tile.getChartData();
int noOfItems = dataList.size();
double sum = dataList.stream().mapToDouble(ChartData::getValue).sum();
double stepSize = 360.0 / sum;
double barWidth = chartCanvas.getWidth() * 0.1;
double ri = outerRadius - barWidth * 0.5;
double ro = outerRadius + barWidth * 0.5;
for (int i = 0 ; i < noOfItems ; i++) {
ChartData data = dataList.get(i);
double value = data.getValue();
startAngle -= angle;
angle = value * stepSize;
boolean hit = Helper.isInRingSegment(x, y, centerX, centerY, ro, ri, startAngle, angle);
if (hit) {
String tooltipText = new StringBuilder(data.getName()).append("\n").append(String.format(locale, formatString, value)).toString();
Point2D popupLocation = new Point2D(e.getScreenX() - selectionTooltip.getWidth() * 0.5, e.getScreenY() - size * 0.025 - selectionTooltip.getHeight());
selectionTooltip.setText(tooltipText);
selectionTooltip.setX(popupLocation.getX());
selectionTooltip.setY(popupLocation.getY());
selectionTooltip.show(tile.getScene().getWindow());
tile.fireTileEvent(new TileEvent(EventType.SELECTED_CHART_DATA, data)); break;
}
}
};
moveHandler = e -> selectionTooltip.hide();
titleText = new Text();
titleText.setFill(tile.getTitleColor());
Helper.enableNode(titleText, !tile.getTitle().isEmpty());
text = new Text(tile.getText());
text.setFill(tile.getTextColor());
Helper.enableNode(text, tile.isTextVisible());
chartCanvas = new Canvas(size * 0.9, tile.isTextVisible() ? height - size * 0.28 : height - size * 0.205);
chartCtx = chartCanvas.getGraphicsContext2D();
legendCanvas = new Canvas(size * 0.225, tile.isTextVisible() ? height - size * 0.28 : height - size * 0.205);
legendCtx = legendCanvas.getGraphicsContext2D();
selectionTooltip = new Tooltip("");
selectionTooltip.setWidth(60);
selectionTooltip.setHeight(48);
Tooltip.install(chartCanvas, selectionTooltip);
getPane().getChildren().addAll(titleText, legendCanvas, chartCanvas, text);
}
@Override protected void registerListeners() {
super.registerListeners();
tile.getChartData().addListener(chartDataListener);
chartCanvas.addEventHandler(MouseEvent.MOUSE_PRESSED, clickHandler);
chartCanvas.addEventHandler(MouseEvent.MOUSE_MOVED, moveHandler);
}
// ******************** Methods *******************************************
@Override protected void handleEvents(final String EVENT_TYPE) {
super.handleEvents(EVENT_TYPE);
if ("VISIBILITY".equals(EVENT_TYPE)) {
Helper.enableNode(titleText, !tile.getTitle().isEmpty());
Helper.enableNode(text, tile.isTextVisible());
double chartCanvasWidth = width - size * 0.1;
double chartCanvasHeight = tile.isTextVisible() ? height - size * 0.28 : height - size * 0.205;
double chartCanvasSize = chartCanvasWidth < chartCanvasHeight ? chartCanvasWidth : chartCanvasHeight;
double legendCanvasWidth = width * 0.225;
double legendCanvasHeight = chartCanvasSize;
chartCanvas.setWidth(chartCanvasSize);
chartCanvas.setHeight(chartCanvasSize);
legendCanvas.setWidth(legendCanvasWidth);
legendCanvas.setHeight(legendCanvasHeight);
}
}
@Override public void dispose() {
tile.getChartData().removeListener(chartDataListener);
tile.getChartData().forEach(chartData -> chartData.removeChartDataEventListener(chartEventListener));
chartCanvas.removeEventHandler(MouseEvent.MOUSE_PRESSED, clickHandler);
chartCanvas.removeEventHandler(MouseEvent.MOUSE_MOVED, moveHandler);
super.dispose();
}
private void drawChart() {
List dataList = tile.isSortedData() ? tile.getChartData().stream().sorted(Comparator.comparingDouble(ChartData::getValue)).collect(Collectors.toList()) : tile.getChartData();
double canvasSize = chartCanvas.getWidth();
int noOfItems = dataList.size();
double center = canvasSize * 0.5;
double barWidth = canvasSize * 0.1;
double sum = dataList.stream().mapToDouble(ChartData::getValue).sum();
double stepSize = 360.0 / sum;
double angle = 0;
double startAngle = 90;
double xy = canvasSize * 0.1;
double wh = canvasSize * 0.8;
//Color bkgColor = tile.getBackgroundColor();
Color textColor = tile.getTextColor();
centerX = xy + wh * 0.5;
centerY = xy + wh * 0.5;
innerRadius = canvasSize * 0.275;
outerRadius = canvasSize * 0.5;
chartCtx.clearRect(0, 0, canvasSize, canvasSize);
chartCtx.setLineCap(StrokeLineCap.BUTT);
chartCtx.setFill(textColor);
chartCtx.setTextBaseline(VPos.CENTER);
chartCtx.setTextAlign(TextAlignment.CENTER);
// Sum
if (tile.isValueVisible()) {
chartCtx.setFont(Fonts.latoRegular(canvasSize * 0.15));
chartCtx.fillText(String.format(Locale.US, "%.0f", sum), center, center, canvasSize * 0.4);
}
chartCtx.setFont(Fonts.latoRegular(barWidth * 0.5));
for (int i = 0 ; i < noOfItems ; i++) {
ChartData data = dataList.get(i);
double value = data.getValue();
startAngle -= angle;
angle = value * stepSize;
// Segment
chartCtx.setLineWidth(barWidth);
chartCtx.setStroke(data.getFillColor());
chartCtx.strokeArc(xy, xy, wh, wh, startAngle, -angle, ArcType.OPEN);
double radValue = Math.toRadians(startAngle - (angle * 0.5));
double cosValue = Math.cos(radValue);
double sinValue = Math.sin(radValue);
// Percentage
double x = innerRadius * cosValue;
double y = -innerRadius * sinValue;
chartCtx.setFill(textColor);
chartCtx.fillText(String.format(Locale.US, "%.0f%%", (value / sum * 100.0)), center + x, center + y, barWidth);
// Value
if (angle > 10) {
x = outerRadius * cosValue;
y = -outerRadius * sinValue;
chartCtx.setFill(data.getTextColor());
chartCtx.fillText(String.format(Locale.US, "%.0f", value), center + x, center + y, barWidth);
}
}
}
private void drawLegend() {
List dataList = tile.getChartData();
double canvasWidth = legendCanvas.getWidth();
double canvasHeight = legendCanvas.getHeight();
int noOfItems = dataList.size();
//List sortedDataList = dataList.stream().sorted(Comparator.comparingDouble(ChartData::getValue).reversed()).collect(Collectors.toList());
Color textColor = tile.getTextColor();
double stepSize = canvasHeight * 0.9 / (noOfItems + 1);
legendCtx.clearRect(0, 0, canvasWidth, canvasHeight);
legendCtx.setTextAlign(TextAlignment.LEFT);
legendCtx.setTextBaseline(VPos.CENTER);
legendCtx.setFont(Fonts.latoRegular(canvasHeight * 0.05));
for (int i = 0 ; i < noOfItems ; i++) {
ChartData data = dataList.get(i);
legendCtx.setFill(data.getFillColor());
legendCtx.fillOval(0, (i + 1) * stepSize, size * 0.0375, size * 0.0375);
legendCtx.setFill(textColor);
legendCtx.fillText(data.getName(), size * 0.05, (i + 1) * stepSize + canvasHeight * 0.025);
}
}
// ******************** Resizing ******************************************
@Override protected void resizeStaticText() {
double maxWidth = width - size * 0.1;
double fontSize = size * textSize.factor;
titleText.setFont(Fonts.latoRegular(fontSize));
if (titleText.getLayoutBounds().getWidth() > maxWidth) { Helper.adjustTextSize(titleText, maxWidth, fontSize); }
switch(tile.getTitleAlignment()) {
default :
case LEFT : titleText.relocate(size * 0.05, size * 0.05); break;
case CENTER: titleText.relocate((width - titleText.getLayoutBounds().getWidth()) * 0.5, size * 0.05); break;
case RIGHT : titleText.relocate(width - (size * 0.05) - titleText.getLayoutBounds().getWidth(), size * 0.05); break;
}
text.setFont(Fonts.latoRegular(fontSize));
if (text.getLayoutBounds().getWidth() > maxWidth) { Helper.adjustTextSize(text, maxWidth, fontSize); }
switch(tile.getTextAlignment()) {
default :
case LEFT : text.setX(size * 0.05); break;
case CENTER: text.setX((width - text.getLayoutBounds().getWidth()) * 0.5); break;
case RIGHT : text.setX(width - (size * 0.05) - text.getLayoutBounds().getWidth()); break;
}
text.setY(height - size * 0.05);
}
@Override protected void resize() {
super.resize();
width = tile.getWidth() - tile.getInsets().getLeft() - tile.getInsets().getRight();
height = tile.getHeight() - tile.getInsets().getTop() - tile.getInsets().getBottom();
size = width < height ? width : height;
double chartCanvasWidth = contentBounds.getWidth();
double chartCanvasHeight = contentBounds.getHeight();
double chartCanvasSize = chartCanvasWidth < chartCanvasHeight ? chartCanvasWidth : chartCanvasHeight;
double legendCanvasWidth = width * 0.225;
double legendCanvasHeight = chartCanvasSize;
if (width > 0 && height > 0) {
pane.setMaxSize(width, height);
pane.setPrefSize(width, height);
legendCanvas.setWidth(legendCanvasWidth);
legendCanvas.setHeight(legendCanvasHeight);
legendCanvas.relocate(contentBounds.getX(), contentBounds.getY());
legendCanvas.setVisible(width > (height * 1.2));
chartCanvas.setWidth(chartCanvasSize);
chartCanvas.setHeight(chartCanvasSize);
if (width > (height * 1.5)) {
chartCanvas.relocate((width - chartCanvasSize) * 0.5, contentBounds.getY() + (contentBounds.getHeight() - chartCanvasSize) * 0.5);
} else if (width > (height * 1.2)) {
chartCanvas.relocate((width - size * 0.05 - chartCanvasSize), contentBounds.getY() + (contentBounds.getHeight() - chartCanvasSize) * 0.5);
} else {
chartCanvas.relocate((width - chartCanvasSize) * 0.5, contentBounds.getY() + (contentBounds.getHeight() - chartCanvasSize) * 0.5);
}
resizeStaticText();
}
}
@Override protected void redraw() {
super.redraw();
titleText.setText(tile.getTitle());
text.setText(tile.getText());
resizeStaticText();
drawLegend();
drawChart();
titleText.setFill(tile.getTitleColor());
text.setFill(tile.getTextColor());
}
}