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

ComboBox: Root observable list changing causes very strange selection issues

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P3 P3
    • 9
    • 8u40, 8u60
    • javafx
    • Windows x64, latest u40 or u60

      (This may or may not be specific to Strings being used as ComboItems)

      Setup:
      A ComboBox contains an observable root list of strings, which is then turned int a sorted list (might matter). The ComboBox has a selection.

      Issue:
      If the root backing list changes where the new list of items still contains the currently selected item, but now at a different location in the list, the combo starts exhibiting very strange behavior when displaying (A) what item is supposedly selected, and (B) what item is selected when it's dropped down. Even if a workaround is attempted to remember the old selection and then re-selecting it once the new list has been set on the observable list, the behavior is wrong and suddenly behaves even differently than the first test case.

      I've created an easy-to-run application to show both issues.

      Step by step using provided sample code:

      Test #1
      1. Run program, leave everything as is. Press the "Change Root List" button. Expected behavior is that "Hello 3" which was selected from the start, would remain selected as the new list also contains "Hello 3". However, instead "Hello 5" is selected. (both are at index 4, but the index != item).

      Test #2
      2. Run program (don't re-use from test #1). Select "Bug 2". This case tries to save/restore the selection. Press the "Change Root List" button. Expected behavior is that "Hello 3" would again be selected. It's however again "Hello 5". This time open the combo popup. Look at the list, and "Hello 3" is selected in the list, despite the combo showing "Hello 5" as the text.

      Perhaps these are two different bugs, but they seem related. I've tested on both latest u40 official and u60.

      TEST CODE:

      package javafx.bugs;

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

      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.collections.transformation.SortedList;
      import javafx.geometry.Insets;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.ComboBox;
      import javafx.scene.control.Label;
      import javafx.scene.control.ListCell;
      import javafx.scene.control.ListView;
      import javafx.scene.control.RadioButton;
      import javafx.scene.control.TextArea;
      import javafx.scene.control.ToggleGroup;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;
      import javafx.util.Callback;

      public class ComboSortSelectionBug extends Application {

      private static final String _StrHello = "Hello ";

      public static void main(String[] args) {
      launch(args);
      }

      @Override
      public void start(Stage primaryStage) throws Exception {
      final ComboBox<String> combo = new ComboBox<>();

      // set up base set of items
      final ObservableList<String> items = FXCollections.observableArrayList();
      for (int i = 0; i < 10; i++) {
      items.add(new String(_StrHello + i));
      }

      // keep list sorted
      SortedList<String> sortedList = items.sorted(new Comparator<String>() {
      @Override
      public int compare(String o1, String o2) {
      return o1.compareTo(o2);
      }
      });

      ToggleGroup g = new ToggleGroup();
      RadioButton rb1 = new RadioButton("Bug 1 - Don't manually preserve/re-set selection (RE-START APPLICATION PER TEST!)");
      RadioButton rb2 = new RadioButton("Bug 2 - Manually preserve/re-set selection (RE-START APPLICATION PER TEST!)");
      rb1.setToggleGroup(g);
      rb2.setToggleGroup(g);
      rb1.setSelected(true);

      combo.setItems(sortedList);
      combo.getSelectionModel().select(_StrHello + 3);

      combo.setButtonCell(new ListCell<String>() {
      @Override
      protected void updateItem(String item, boolean empty) {
      super.updateItem(item, empty);
      setText(empty ? null : item);
      }
      });
      combo.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
      @Override
      public ListCell<String> call(ListView<String> param) {
      return new ListCell<String>() {
      @Override
      protected void updateItem(String item, boolean empty) {
      super.updateItem(item, empty);
      setText(empty ? null : item);
      }
      };
      }
      });

      TextArea out = new TextArea();

      combo.getSelectionModel().selectedItemProperty().addListener((prop, ov, nv) -> {
      out.appendText("Selected item property changed to " + nv + "\n");
      });

      Label howTo = new Label("Press 'Change Root List'. Watch how the combo index you had selected stays selected, but the item in the dropdown is wrong. For the second bug, it tries to manually re-select the item. But drop down the combo on that test to see completely bizarre behavior where the selected value no longer matches the combo value nor the index");
      howTo.setWrapText(true);

      Button b = new Button("Change Root List");
      b.setOnAction(e -> {
      out.appendText(String.format("Pre-list change selected item is '%s' and index is '%s' and combo value '%s' \n", combo.getSelectionModel().getSelectedItem(), combo.getSelectionModel().getSelectedIndex(), combo.getValue()));
      List<String> n = new ArrayList<>();

      for (int i = 2; i < 10; i++) {
      n.add(_StrHello + i);
      }

      String sel = null;
      if (rb2.isSelected()) {
      sel = combo.getSelectionModel().getSelectedItem();
      }
      items.setAll(n);
      if (rb2.isSelected()) {
      combo.getSelectionModel().select(sel);
      }

      out.appendText("Root list was changed to " + n + "\n");
      out.appendText(String.format("Post-list change selected item is '%s' and index is '%s' and combo value '%s' \n", combo.getSelectionModel().getSelectedItem(), combo.getSelectionModel().getSelectedIndex(), combo.getValue()));
      });

      VBox content = new VBox(10);
      content.setPadding(new Insets(20));
      content.getChildren().addAll(howTo, combo, rb1, rb2, b, out);
      Scene scene = new Scene(content, 600, 500);
      primaryStage.setTitle("Combo Selection/List-Change Bug");
      primaryStage.setScene(scene);
      primaryStage.show();
      }

      }

            jgiles Jonathan Giles
            ecrumhornjfx Emil Crumhorn (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: