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

ComboBox popup list view does not scrollTo when ComboBox value/selection is set

    Details

    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 8u45, openjfx14
    • Fix Version/s: tbd
    • Component/s: javafx
    • Labels:
    • Subcomponent:
    • CPU:
      x86
    • OS:
      os_x

      Description

      FULL PRODUCT VERSION :
      java version "1.8.0_60-ea"
      Java(TM) SE Runtime Environment (build 1.8.0_60-ea-b13)
      Java HotSpot(TM) 64-Bit Server VM (build 25.60-b12, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      uname -a

      Darwin PFs-MacBook-Pro.home 14.3.0 Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64 x86_64


      A DESCRIPTION OF THE PROBLEM :
      There are scenarios where the value of a ComboBox (the value, or "selected index") will be set programmatically:

      1. upon initialization with data from a database, etc.;
      2. KeyCombination navigation to increment or decrement the selected index.

      Setting the value or selection of a JavaFX ComboBox programmatically should scroll the popup list view to show the selected item. But it doesn't, and when the user pops down the list view and the selected item is not in view, it's pretty jarring.



      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the executable test case; read comments in the app as it is running.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Setting the value or selection of a JavaFX ComboBox programmatically should scroll the popup list view to show the selected item.
      ACTUAL -
      Setting the value or selection of a JavaFX ComboBox programmatically does NOT scroll the popup list view to show the selected item.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      This bug does not cause crashes. Just unhappy users.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      package org.pf.javafx.comboboxes;

      import javafx.application.Application;
      import javafx.collections.FXCollections;
      import javafx.collections.ObservableList;
      import javafx.event.ActionEvent;
      import javafx.event.EventHandler;
      import javafx.geometry.Insets;
      import javafx.geometry.Pos;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.ComboBox;
      import javafx.scene.control.Label;
      import javafx.scene.control.ListView;
      import javafx.scene.control.RadioButton;
      import javafx.scene.control.TextField;
      import javafx.scene.control.ToggleGroup;
      import javafx.scene.layout.HBox;
      import javafx.scene.layout.Priority;
      import javafx.scene.layout.VBox;
      import javafx.scene.paint.Color;
      import javafx.scene.text.TextAlignment;
      import javafx.stage.Stage;

      import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;

      /**
       * This small JavaFX app demonstrates how setting a ComboBox's value or selection index/object programmatically does not
       * cause the listview to scroll to the new value. It should.
       *
       * Some scenarios where the value of a ComboBox would be set programmatically:
       *
       * - initial setting based on data pulled into the app;
       * - assigning keycombinations to invoke selectNext() and selectPrevious() on the ComboBox's selectionModel.
       *
       *
       * @author Paul Furbacher
       *
       */
      public class ComboBoxSelectionTest extends Application {

          private static final int PREFERRED_WIDTH = 600;

          @Override
          public void start(Stage stage) throws Exception {
              stage.setTitle("ComboBox programmatic selection test");

              Insets insets = new Insets(12, 12, 12, 12);

              Label description = new Label(
                  "Setting the selected index of a combo box does not cause the "
                      + "list in the popup to scroll the selected item into view?!\n\n"
                      + "This fails to happen whether you call ComboBox.setValue() or ComboBox.getSelectionModel().select().");
              description.setWrapText(true);
              description.setPrefWidth(500);

              final HBox hboxDescription = createHBoxWithContent(12.0, insets, Pos.CENTER, description);

              final ToggleGroup toggleGroup = new ToggleGroup();
              final RadioButton rb1 = createRadioButtonForGroup("use 'ComboBox.setValue(...)'", toggleGroup);
              final RadioButton rb2 = createRadioButtonForGroup("use 'ComboBox.getSelectionModel().select(...)'", toggleGroup);
              toggleGroup.selectToggle(rb1);

              final VBox vboxRadioButtons = createVBoxWithContent(12.0, insets, Pos.CENTER_LEFT, rb1, rb2);
              final HBox hboxRadioButtons = createHBoxWithContent(12.0, insets, Pos.CENTER, vboxRadioButtons);

              final ObservableList<String> items = getItemsForComboBoxes();

              final ComboBox<String> cb1 = new ComboBox<>(items);
              cb1.getSelectionModel().select(0);
              cb1.setPrefSize(80, 24);

              // This combobox will demonstrate the "correct" behavior when setting value / selection.
              // See "skin.getPopupContent()).scrollTo(index)" below in the setOnAction() method.
              final ComboBox<String> cb2 = new ComboBox<>(items);
              cb2.getSelectionModel().select(0);
              cb2.setPrefSize(80, 24);

              final Label label = new Label("Enter index # (0-99); press 'Return': ");
              final TextField comboIndexTF = new TextField("");
              comboIndexTF.setPrefSize(50, 24);

              comboIndexTF.setOnAction(new EventHandler<ActionEvent>() {
                  @Override
                  public void handle(ActionEvent event) {
                      try {
                          int index = Integer.valueOf(comboIndexTF.getText());
                          if (index < items.size()) {
                              if (rb1.isSelected()) {
                                  cb1.setValue(String.valueOf(index));
                                  cb2.setValue(String.valueOf(index));
                              }
                              else {
                                  cb1.getSelectionModel().select(index);
                                  cb2.getSelectionModel().select(index);
                              }
                          }
                          else {
                              comboIndexTF.setText("");
                          }

                          // Should we really have to do the following?!
                          // This causes the listview to scrollTo the new value.
                          ComboBoxListViewSkin<?> skin = (ComboBoxListViewSkin<?>) cb2.getSkin();
                          ((ListView<?>) skin.getPopupContent()).scrollTo(index);
                      } catch (NumberFormatException e) {
                          comboIndexTF.setText("");
                      }
                  }
              });

              final VBox vBoxDefaultBehavior = new VBox(new Label("default"), cb1);
              vBoxDefaultBehavior.setAlignment(Pos.BOTTOM_CENTER);

              final Label label2 = new Label("calling skin's \n'scrollTo'");
              label2.setTextAlignment(TextAlignment.CENTER);
              
              final VBox vBoxScrollTo = new VBox(label2, cb2);
              vBoxScrollTo.setAlignment(Pos.BOTTOM_CENTER);
              
              final HBox hbox3 = createHBoxWithContent(12.0, insets, Pos.BOTTOM_CENTER, label, comboIndexTF, vBoxDefaultBehavior, vBoxScrollTo);

              final Label instructions =
                  new Label(
                      "After pressing 'Return', inspect the list for each combo box. "
                          + "Even though the 'button cell' displays the selected value in "
                          + "both combo boxes, the list in the left one has not been scrolled to the "
                          + "selected index. The right one has been scrolled programmatically via the 'private' skin API. \n\n"
                          + "Should it really be necessary to dig into the 'private' skin API to have the "
                          + "combo box selection do the expected thing in the drop-down list?\n\n"
                          );
              instructions.setWrapText(true);
              instructions.setPrefWidth(500);

              final HBox hbox4 = createHBoxWithContent(12.0, insets, Pos.CENTER, instructions);

              final VBox vbox = new VBox(24, hboxDescription, hboxRadioButtons, hbox3, hbox4);
              VBox.setVgrow(vboxRadioButtons, Priority.NEVER);
              vbox.setPadding(new Insets(0, 0, 12, 0));
              vbox.setPrefWidth(PREFERRED_WIDTH);

              final Scene scene = new Scene(vbox);
              scene.setFill(Color.WHITE);
              stage.setScene(scene);
              stage.sizeToScene();
              stage.setResizable(false);
              stage.show();
          }

          protected RadioButton createRadioButtonForGroup(String text, ToggleGroup toggleGroup) {
              RadioButton rb1 = new RadioButton(text);
              rb1.setToggleGroup(toggleGroup);
              return rb1;
          }

          protected HBox createHBoxWithContent(double spacing, Insets padding, Pos alignment, Node... nodes) {
              HBox hBox = new HBox();
              hBox.setSpacing(spacing);
              hBox.setAlignment(alignment);
              hBox.setPadding(padding);
              hBox.getChildren().addAll(nodes);
              return hBox;
          }
          
          protected VBox createVBoxWithContent(double spacing, Insets padding, Pos alignment, Node...nodes) {
              final VBox vbox = new VBox(spacing, nodes);
              vbox.setAlignment(alignment);
              vbox.setPadding(padding);
              vbox.setPrefWidth(500);
              return vbox;
          }

          protected ObservableList<String> getItemsForComboBoxes() {
              String[] values = new String[100];
              for (int i = 0; i < values.length; i++) {
                  values[i] = String.valueOf(i);
              }

              ObservableList<String> items = FXCollections.observableArrayList(values);
              return items;
          }

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

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      As shown in the executable test case:

           ComboBoxListViewSkin<?> skin = (ComboBoxListViewSkin<?>) combobox.getSkin();
           ((ListView<?>) skin.getPopupContent()).scrollTo(index);

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                aghaisas Ajit Ghaisas
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                1 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated: