tilesfx icon indicating copy to clipboard operation
tilesfx copied to clipboard

Tooltips on Data Points

Open bgmf opened this issue 2 years ago • 1 comments

Hi Gerrit

If you create a Tile with a XYChart in it (Line or Area, doesn't matter) you can use JavaFX's XYChart.Data class to add items to the graph. In its documentation it is stated, that the node for each data point is required to be set manually either before you add it to the series it belongs to, or it will be automatically created. But in the library that seems not to be the case, therefore we are not able to set a tooltip on a point.

Here's a simple example to test it (tested against version 16.0.3):

import eu.hansolo.tilesfx.Tile;
import eu.hansolo.tilesfx.TileBuilder;
import eu.hansolo.tilesfx.chart.TilesFXSeries;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;

import java.util.Optional;
import java.util.Random;

public class ChartTest extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        var tile = TileBuilder.create().skinType(Tile.SkinType.SMOOTHED_CHART).chartType(Tile.ChartType.LINE).backgroundColor(Color.TRANSPARENT)
                .unitColor(Color.GRAY).valueColor(Color.GRAY).tickLabelColor(Color.GRAY).minSize(150.0, 250.0).prefSize(150.0, 250.0).build();
        tile.getXAxis().setLabel("X Axis");
        tile.getYAxis().setLabel("Y Axis");

        Optional.ofNullable(tile.getXAxis().lookup(".axis-label")).ifPresent(it -> {
            if (it instanceof Label l)
                l.setTextAlignment(TextAlignment.CENTER);
        });
        Optional.ofNullable(tile.getYAxis().lookup(".axis-label")).ifPresent(it -> {
            if (it instanceof Label l)
                l.setTextAlignment(TextAlignment.CENTER);
        });

        var seriesA = new XYChart.Series<String, Number>();
        seriesA.setName("A");

        var seriesB = new XYChart.Series<String, Number>();
        seriesB.setName("B");

        tile.setTilesFXSeries(new TilesFXSeries<>(seriesA, Color.RED), new TilesFXSeries<>(seriesB, Color.GREEN));

        seriesA.getData().add(new XYChart.Data("", 0));
        seriesB.getData().add(new XYChart.Data("", 0));

        var r = new Random();
        for (int i = 1; i < 10; i++) {
            seriesA.getData().add(new XYChart.Data("" + i, r.nextInt(100)));
            seriesB.getData().add(new XYChart.Data("" + i, r.nextInt(100)));
        }

        for (var data : seriesA.getData()) {
            System.err.println(data.getNode());
            // from the documentation you can either set a node, before adding the data to the series
            // OR: it should be provided by the implementation (like a bullet or something)
            
            // Tooltip installation will fail silently, since data.getNode() returns null
            // Tooltip.install(data.getNode(), new Tooltip(data.getXValue() + ": " + data.getYValue()));
        }
        for (var data : seriesB.getData()) {
            System.err.println(data.getNode());
        }

        var box = new VBox(10.0);
        box.getChildren().add(tile);

        var scene = new Scene(box, 500.0, 250.0);
        stage.setScene(scene);
        stage.setMinWidth(500.0);
        stage.setMinHeight(250.0);

        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(ChartTest.class, args);
    }
}

Thanks in advance for any tips, hacks or workarounds... :wink:

Cheers, Daniel

bgmf avatar Aug 26 '21 10:08 bgmf

Ok, I think I've found the problem, I've pushed the fix with commit: 8a40e0822815c521d1773a9071b217cb13835034 Could you give it a try and let me know if it works? With this version it should be possible to add a tooltip to a data point as follows:

public void start(Stage stage) throws Exception {
        var tile = TileBuilder.create()
                              .skinType(Tile.SkinType.SMOOTHED_CHART)
                              .chartType(Tile.ChartType.LINE)
                              .backgroundColor(Color.TRANSPARENT)
                              .unitColor(Color.GRAY)
                              .valueColor(Color.GRAY)
                              .tickLabelColor(Color.GRAY)
                              .minSize(150.0, 250.0)
                              .prefSize(150.0, 250.0)
                              .build();
        tile.getXAxis().setLabel("X Axis");
        tile.getYAxis().setLabel("Y Axis");

        Optional.ofNullable(tile.getXAxis().lookup(".axis-label")).ifPresent(it -> {
            if (it instanceof Label l) { l.setTextAlignment(TextAlignment.CENTER); }
        });
        Optional.ofNullable(tile.getYAxis().lookup(".axis-label")).ifPresent(it -> {
            if (it instanceof Label l) { l.setTextAlignment(TextAlignment.CENTER); }
        });

        var seriesA = new XYChart.Series<String, Number>();
        seriesA.setName("A");

        var seriesB = new XYChart.Series<String, Number>();
        seriesB.setName("B");

        var tilesFXSeriesA = new TilesFXSeries<>(seriesA, Color.RED);
        var tilesFXSeriesB = new TilesFXSeries<>(seriesB, Color.LIME);

        tile.setTilesFXSeries(tilesFXSeriesA, tilesFXSeriesB);

        seriesA.getData().add(new XYChart.Data("", 0));
        seriesB.getData().add(new XYChart.Data("", 0));

        var r = new Random();
        for (int i = 1; i < 10; i++) {
            XYChart.Data dataA = new Data("" + i, r.nextInt(100));
            dataA.setNode(new Circle(5, tilesFXSeriesA.getFill()));
            Tooltip.install(dataA.getNode(), new Tooltip("Tooltip A: " + i));
            seriesA.getData().add(dataA);

            XYChart.Data dataB = new Data("" + i, r.nextInt(100));
            dataB.setNode(new Rectangle(10, 10, tilesFXSeriesB.getFill()));
            Tooltip.install(dataB.getNode(), new Tooltip("Tooltip B: " + i));
            seriesB.getData().add(dataB);

            //seriesA.getData().add(new XYChart.Data("" + i, r.nextInt(100)));
            //seriesB.getData().add(new XYChart.Data("" + i, r.nextInt(100)));
        }

        for (var data : seriesA.getData()) {
            System.err.println(data.getNode());
            // from the documentation you can either set a node, before adding the data to the series
            // OR: it should be provided by the implementation (like a bullet or something)

            // Tooltip installation will fail silently, since data.getNode() returns null
            // Tooltip.install(data.getNode(), new Tooltip(data.getXValue() + ": " + data.getYValue()));
        }
        for (var data : seriesB.getData()) {
            System.err.println(data.getNode());
        }

        var box = new VBox(10.0);
        box.setBackground(new Background(new BackgroundFill(Tile.BACKGROUND, CornerRadii.EMPTY, Insets.EMPTY)));
        box.getChildren().add(tile);

        var scene = new Scene(box, 500.0, 250.0);
        stage.setScene(scene);
        stage.setMinWidth(500.0);
        stage.setMinHeight(250.0);

        stage.show();
    }

HanSolo avatar Aug 26 '21 12:08 HanSolo