package io.haskins.java.cloudtrailviewer.utils;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.chart.Chart;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Tooltip;
import javafx.scene.input.MouseEvent;
import java.util.Collection;
import java.util.function.Function;
/**
* Adapted from code found here : http://stackoverflow.com/a/38367201
*
* Created by markhaskins on 14/02/2017.
*/
public class ChartHoverUtil<T> {
public static void setupPieChartHovering(Chart chart) {
if (chart instanceof PieChart) {
new ChartHoverUtil<PieChart.Data>(
data -> String.format("Value = %s", data.getPieValue()),
PieChart.Data::getNode)
.setupHovering(((PieChart)chart).getData());
}
if (chart instanceof XYChart) {
new ChartHoverUtil<XYChart.Series<String, Number>>(
data -> String.format("Value = %s", data.getData().get(0).getYValue()),
data -> data.getNode())
.setupHovering(((XYChart)chart).getData());
}
}
private final Tooltip tooltip = new Tooltip();
private final SimpleBooleanProperty adjustingTooltip = new SimpleBooleanProperty(false);
private final Function<T, String> textProvider;
private final Function<T, Node> nodeProvider;
private EventHandler<MouseEvent> moveHandler = e -> {
if (tooltip.isShowing()) {
setLabelPosition(e);
}
};
private EventHandler<MouseEvent> enterHandler = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
adjustingTooltip.set(true);
Node chartNode = (Node) e.getSource();
tooltip.show(chartNode, e.getScreenX(), e.getScreenY());
setLabelPosition(e);
ObservableBooleanValue stillHovering = chartNode.hoverProperty().or(adjustingTooltip);
stillHovering.addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean nowHovering) {
if (!nowHovering) {
stillHovering.removeListener(this);
tooltip.hide();
}
}
});
T chartData = (T) chartNode.getUserData();
String txt = textProvider.apply(chartData);
tooltip.setText(txt);
adjustingTooltip.set(false);
}
};
private ChartHoverUtil(Function<T, String> textProvider, Function<T, Node> getNode) {
this.textProvider = textProvider;
this.nodeProvider = getNode;
tooltip.addEventFilter(MouseEvent.MOUSE_MOVED, moveHandler);
}
private void setupHovering(Collection<T> data) {
for (T chartData : data) {
Node node = nodeProvider.apply(chartData);
if (node != null) {
node.setUserData(chartData);
setupNodeHovering(node);
}
}
}
private void setupNodeHovering(Node node) {
node.addEventFilter(MouseEvent.MOUSE_MOVED, moveHandler);
node.addEventHandler(MouseEvent.MOUSE_ENTERED, enterHandler);
}
private void setLabelPosition(MouseEvent e) {
adjustingTooltip.set(true);
tooltip.setAnchorX(e.getScreenX());
tooltip.setAnchorY(e.getScreenY() + 20);
adjustingTooltip.set(false);
}
}