Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8209778

XYChart assigns wrong default-colorX style-classes on replacing all series

    Details

    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 8, 9, 10, openjfx11
    • Fix Version/s: tbd
    • Component/s: javafx
    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      generic

      Description

      A DESCRIPTION OF THE PROBLEM :
      When I replace all Series with new series, the new series' data-elements get wrong style-classes for default-color. For the first 8 series, the numbering is correct, for all other, the numbering is shifted.
      See here for a detailed description:
      https://stackoverflow.com/questions/51893749/javafx-xycharts-weird-behaviour-on-replacing-all-series

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      import javafx.application.Application;
      import javafx.beans.InvalidationListener;
      import javafx.scene.Scene;
      import javafx.scene.chart.NumberAxis;
      import javafx.scene.chart.ScatterChart;
      import javafx.scene.chart.XYChart;
      import javafx.scene.control.Button;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      import java.util.ArrayList;
      import java.util.List;

      public class ChartSeriesReplacing extends Application {
          @Override
          public void start(Stage primaryStage) throws Exception {

              VBox root = new VBox();

              ScatterChart<Number, Number> chart = new ScatterChart<>(new NumberAxis(), new NumberAxis());

              Button refresh = new Button("Refresh");

              refresh.setOnAction(clicked -> {
                  List<XYChart.Series<Number, Number>> seriesList = new ArrayList<>();
                  for (int i = 0; i < 15; i++) {
                      XYChart.Series<Number, Number> series = new XYChart.Series<>();
                      series.setName(i + "");

                      XYChart.Data<Number, Number> data = new XYChart.Data<>(Math.random(), Math.random());
                      int finalI = i;
                      data.nodeProperty().addListener(__ -> {
                          if (data.getNode() != null) {
                              data.getNode().getStyleClass().addListener((InvalidationListener) ___ -> {

                                  // System.out.println("[pre] Data #" + finalI + ": Style = " + data.getNode().getStyleClass());

                                  // DIRTY-WORKAROUND-SOLUTION:
                                  // (has to live in listener, otherwise the chart erases any custom styling on adding... :-/ )
                                  // String colorString = "default-color" + finalI % 8;
                                  // data.getNode().getStyleClass().removeIf(s -> s.startsWith("default-color") && !s.equals(colorString));
                                  // if (!data.getNode().getStyleClass().contains(colorString)) {
                                  // data.getNode().getStyleClass().add(colorString);
                                  // }
                                  // --------------------------

                                  System.out.println("[post] Data #" + finalI + ": Style = " + data.getNode().getStyleClass());
                              });
                          }
                      });
                      series.getData().add(data);

                      seriesList.add(series);
                  }
                  System.out.println("-----------------");

                  // What I tried:
                  // 1)
                  chart.getData().setAll(seriesList);

                  // 2)
                  // chart.dataProperty().setValue(FXCollections.observableArrayList(seriesList));

                  // 3)
                  // chart.getData().clear();
                  // 3a)
                  // chart.getData().addAll(seriesList);
                  // 3b)
                  // seriesList.forEach(series->chart.getData().add(series));
              });


              root.getChildren().add(chart);
              root.getChildren().add(refresh);

              primaryStage.setScene(new Scene(root));
              primaryStage.show();
          }
      }


      // --> hit "refresh" multiple times

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      [post] Data #0: Style = chart-symbol series0 data0 default-color0
      [post] Data #1: Style = chart-symbol series1 data0 default-color1
      [post] Data #2: Style = chart-symbol series2 data0 default-color2
      [post] Data #3: Style = chart-symbol series3 data0 default-color3
      [post] Data #4: Style = chart-symbol series4 data0 default-color4
      [post] Data #5: Style = chart-symbol series5 data0 default-color5
      [post] Data #6: Style = chart-symbol series6 data0 default-color6
      [post] Data #7: Style = chart-symbol series7 data0 default-color7
      [post] Data #8: Style = chart-symbol series8 data0 default-color0
      [post] Data #9: Style = chart-symbol series9 data0 default-color1
      [post] Data #10: Style = chart-symbol series10 data0 default-color2
      [post] Data #11: Style = chart-symbol series11 data0 default-color3
      [post] Data #12: Style = chart-symbol series12 data0 default-color4
      [post] Data #13: Style = chart-symbol series13 data0 default-color5
      [post] Data #14: Style = chart-symbol series14 data0 default-color6

      for every click on "refresh"
      ACTUAL -
      for 1st click as expected.

      for 2nd click:

      [post] Data #1: Style = chart-symbol series1 data0 default-color0
      [post] Data #2: Style = chart-symbol series2 data0 default-color1
      [post] Data #3: Style = chart-symbol series3 data0 default-color2
      [post] Data #4: Style = chart-symbol series4 data0 default-color3
      [post] Data #5: Style = chart-symbol series5 data0 default-color4
      [post] Data #6: Style = chart-symbol series6 data0 default-color5
      [post] Data #7: Style = chart-symbol series7 data0 default-color6
      [post] Data #8: Style = chart-symbol series8 data0 default-color7
      [post] Data #9: Style = chart-symbol series9 data0 default-color7 <-- from here on shifted!
      [post] Data #10: Style = chart-symbol series10 data0 default-color0
      [post] Data #11: Style = chart-symbol series11 data0 default-color1
      [post] Data #12: Style = chart-symbol series12 data0 default-color2
      [post] Data #13: Style = chart-symbol series13 data0 default-color3
      [post] Data #14: Style = chart-symbol series14 data0 default-color4
      [post] Data #0: Style = chart-symbol series0 data0 default-color0

      for 3rd click:

      [post] Data #1: Style = chart-symbol series1 data0 default-color0
      [post] Data #2: Style = chart-symbol series2 data0 default-color1
      [post] Data #3: Style = chart-symbol series3 data0 default-color2
      [post] Data #4: Style = chart-symbol series4 data0 default-color3
      [post] Data #5: Style = chart-symbol series5 data0 default-color4
      [post] Data #6: Style = chart-symbol series6 data0 default-color5
      [post] Data #7: Style = chart-symbol series7 data0 default-color6
      [post] Data #8: Style = chart-symbol series8 data0 default-color7
      [post] Data #9: Style = chart-symbol series9 data0 default-color5 <-- !!
      [post] Data #10: Style = chart-symbol series10 data0 default-color6
      [post] Data #11: Style = chart-symbol series11 data0 default-color7
      [post] Data #12: Style = chart-symbol series12 data0 default-color0
      [post] Data #13: Style = chart-symbol series13 data0 default-color1
      [post] Data #14: Style = chart-symbol series14 data0 default-color2
      [post] Data #0: Style = chart-symbol series0 data0 default-color0

      ---------- BEGIN SOURCE ----------
      import javafx.application.Application;
      import javafx.beans.InvalidationListener;
      import javafx.scene.Scene;
      import javafx.scene.chart.NumberAxis;
      import javafx.scene.chart.ScatterChart;
      import javafx.scene.chart.XYChart;
      import javafx.scene.control.Button;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      import java.util.ArrayList;
      import java.util.List;

      public class ChartSeriesReplacing extends Application {
          @Override
          public void start(Stage primaryStage) throws Exception {

              VBox root = new VBox();

              ScatterChart<Number, Number> chart = new ScatterChart<>(new NumberAxis(), new NumberAxis());

              Button refresh = new Button("Refresh");

              refresh.setOnAction(clicked -> {
                  List<XYChart.Series<Number, Number>> seriesList = new ArrayList<>();
                  for (int i = 0; i < 15; i++) {
                      XYChart.Series<Number, Number> series = new XYChart.Series<>();
                      series.setName(i + "");

                      XYChart.Data<Number, Number> data = new XYChart.Data<>(Math.random(), Math.random());
                      int finalI = i;
                      data.nodeProperty().addListener(__ -> {
                          if (data.getNode() != null) {
                              data.getNode().getStyleClass().addListener((InvalidationListener) ___ -> {

                                  // System.out.println("[pre] Data #" + finalI + ": Style = " + data.getNode().getStyleClass());

                                  // DIRTY-WORKAROUND-SOLUTION:
                                  // (has to live in listener, otherwise the chart erases any custom styling on adding... :-/ )
                                  // String colorString = "default-color" + finalI % 8;
                                  // data.getNode().getStyleClass().removeIf(s -> s.startsWith("default-color") && !s.equals(colorString));
                                  // if (!data.getNode().getStyleClass().contains(colorString)) {
                                  // data.getNode().getStyleClass().add(colorString);
                                  // }
                                  // --------------------------

                                  System.out.println("[post] Data #" + finalI + ": Style = " + data.getNode().getStyleClass());
                              });
                          }
                      });
                      series.getData().add(data);

                      seriesList.add(series);
                  }
                  System.out.println("-----------------");

                  // What I tried:
                  // 1)
                  chart.getData().setAll(seriesList);

                  // 2)
                  // chart.dataProperty().setValue(FXCollections.observableArrayList(seriesList));

                  // 3)
                  // chart.getData().clear();
                  // 3a)
                  // chart.getData().addAll(seriesList);
                  // 3b)
                  // seriesList.forEach(series->chart.getData().add(series));
              });


              root.getChildren().add(chart);
              root.getChildren().add(refresh);

              primaryStage.setScene(new Scene(root));
              primaryStage.show();
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      uncomment the lines titeled dirty workaround.

      FREQUENCY : always


        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated: