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

Tree/TableView: selectPrevious stuck in last row if cellSelection enabled

    Details

    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      linux_ubuntu

      Description

      ADDITIONAL SYSTEM INFORMATION :
      Ubuntu Linux 18.04
      java version "1.8.0_201"
      Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
      Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

      A DESCRIPTION OF THE PROBLEM :
      If you configure the TreeTableView in single-selection mode and you set it so that cell-selection is enabled you get a bug when calling the selectPrevious() method on the selection-model when one of the cells in the last row is selected. If you repeatedly call selectPrevious() the selected cell will not wrap around to the second-last row as you would expect.

      REGRESSION : Last worked in version 8u231

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1.Create a TreeTableView and populate with multiple row and column data.
      2. Set selection-mode to SelectionMode.SINGLE.
      3. Enable cell-selection mode.
      4. Select a cell in the last row.
      5. Have a button that calls selectionModel.selectPrevious() and click it repeatedly.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Selection should have wrapped around to the second-last row. This behavior can be seen by clicking a cell in any of the other rows and you'll see the desired behavior when selectPrevious() is called repeatedly.
      ACTUAL -
      Selection will be stuck in the last row and will not wrap around to second-last row.

      ---------- BEGIN SOURCE ----------
      import java.util.List;

      import javafx.application.Application;
      import javafx.beans.property.IntegerProperty;
      import javafx.beans.property.SimpleIntegerProperty;
      import javafx.beans.property.SimpleStringProperty;
      import javafx.beans.property.StringProperty;
      import javafx.scene.Node;
      import javafx.scene.Scene;
      import javafx.scene.control.Button;
      import javafx.scene.control.SelectionMode;
      import javafx.scene.control.TreeItem;
      import javafx.scene.control.TreeTableColumn;
      import javafx.scene.control.TreeTableView;
      import javafx.scene.control.TreeTableView.TreeTableViewSelectionModel;
      import javafx.scene.control.cell.TextFieldTreeTableCell;
      import javafx.scene.control.cell.TreeItemPropertyValueFactory;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.FlowPane;
      import javafx.stage.Stage;
      import javafx.util.converter.IntegerStringConverter;

      public class TreeTableViewTest extends Application {
          
          public static class Person {
              
              private final StringProperty nameProperty;
              private final StringProperty surnameProperty;
              
              public Person() {
                  this.nameProperty = new SimpleStringProperty();
                  this.surnameProperty = new SimpleStringProperty();
              }
              
              public StringProperty nameProperty() {
                  return this.nameProperty;
              }
              
              public void setName(String value) {
                  this.nameProperty.set(value);
              }
              
              public String getName() {
                  return this.nameProperty.get();
              }
              
              public StringProperty surnameProperty() {
                  return this.surnameProperty;
              }
              
              public void setSurname(String value) {
                  this.surnameProperty.set(value);
              }
              
              public String getSurname() {
                  return this.surnameProperty.get();
              }
          }
          
          public static class Dog {
              
              private final StringProperty nameProperty;
              private final IntegerProperty ageProperty;
              private final StringProperty breedProperty;
              
              public Dog() {
                  this.nameProperty = new SimpleStringProperty();
                  this.ageProperty = new SimpleIntegerProperty();
                  this.breedProperty = new SimpleStringProperty();
              }
              
              public StringProperty nameProperty() {
                  return this.nameProperty;
              }
              
              public void setName(String value) {
                  this.nameProperty.set(value);
              }
              
              public String getName() {
                  return this.nameProperty.get();
              }
              
              public IntegerProperty ageProperty() {
                  return this.ageProperty;
              }
              
              public void setAge(int value) {
                  this.ageProperty.setValue(value);
              }
              
              public int getAge() {
                  return this.ageProperty.get();
              }
              
              public StringProperty breedProperty() {
                  return this.breedProperty;
              }
              
              public void setBreed(String breed) {
                  this.breedProperty.set(breed);
              }
              
              public String getBreed() {
                  return this.breedProperty.get();
              }
          }
          
          private TreeItem<Object> createTreeItems() {
              TreeItem<Object> rootItem = new TreeItem<>();
              
              List<TreeItem<Object>> rootChildren = rootItem.getChildren();
              
              Person john = new Person();
              john.setName("John");
              john.setSurname("Denver");
              TreeItem<Object> johnTreeItem = new TreeItem<>(john);
              
              rootChildren.add(johnTreeItem);
              
              List<TreeItem<Object>> johnChildren = johnTreeItem.getChildren();
              
              Dog charlie = new Dog();
              charlie.setName("Charlie");
              charlie.setAge(4);
              charlie.setBreed("Labrador");
              TreeItem<Object> charlieTreeItem = new TreeItem<>(charlie);
              johnChildren.add(charlieTreeItem);
              
              Dog daisy = new Dog();
              daisy.setName("Daisy");
              daisy.setAge(7);
              daisy.setBreed("Bulldog");
              TreeItem<Object> daisyTreeItem = new TreeItem<>(daisy);
              johnChildren.add(daisyTreeItem);
              
              Person alice = new Person();
              alice.setName("Alice");
              alice.setSurname("Goodhead");
              TreeItem<Object> aliceTreeItem = new TreeItem<>(alice);
              
              rootChildren.add(aliceTreeItem);
              
              List<TreeItem<Object>> aliceChildren = aliceTreeItem.getChildren();
              
              Dog buck = new Dog();
              buck.setName("Buck");
              buck.setAge(1);
              buck.setBreed("Border Collie");
              TreeItem<Object> buckTreeItem = new TreeItem<>(buck);
              aliceChildren.add(buckTreeItem);
              
              return rootItem;
          }
          
          @Override
          public void start(Stage primaryStage) throws Exception {
              TreeTableView<Object> treeTableView = new TreeTableView<>();
              
              TreeTableViewSelectionModel<Object> selectionModel = treeTableView.getSelectionModel();
              selectionModel.setSelectionMode(SelectionMode.SINGLE);
              selectionModel.setCellSelectionEnabled(true);
              
              treeTableView.setEditable(true);
              
              List<TreeTableColumn<Object, ?>> columns = treeTableView.getColumns();
              
              TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
              nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
              nameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
              columns.add(nameColumn);
              
              TreeTableColumn<Object, String> surnameColumn = new TreeTableColumn<>("Surname");
              surnameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
              surnameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("surname"));
              columns.add(surnameColumn);
              
              TreeTableColumn<Object, Integer> ageColumn = new TreeTableColumn<>("Age");
              ageColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn(new IntegerStringConverter()));
              ageColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("age"));
              columns.add(ageColumn);
              
              TreeTableColumn<Object, String> breedColumn = new TreeTableColumn<>("Breed");
              breedColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
              breedColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("breed"));
              columns.add(breedColumn);
              
              TreeItem<Object> rootTreeItem = createTreeItems();
              treeTableView.setRoot(rootTreeItem);
              treeTableView.setShowRoot(false);
              
              TreeTableViewSelectionModel<Object> currentSelectionModel = treeTableView.getSelectionModel();
              currentSelectionModel.clearSelection();
              
              BorderPane mainPane = new BorderPane();
              mainPane.setCenter(treeTableView);
              
              FlowPane buttonPane = new FlowPane();
              List<Node> buttons = buttonPane.getChildren();
              
              Button nextButton = new Button("Next");
              nextButton.setOnAction(e -> currentSelectionModel.selectNext());
              buttons.add(nextButton);
              
              Button previousButton = new Button("Previous");
              previousButton.setOnAction(e -> currentSelectionModel.selectPrevious());
              buttons.add(previousButton);
              
              mainPane.setBottom(buttonPane);
              
              Scene scene = new Scene(mainPane, 800, 600);
              primaryStage.setScene(scene);
              
              primaryStage.show();
          }
          
          public static void main(String[] args) {
              Application.launch(args);
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      There is no easy workaround for this bug. The bug is actually located in the TreeTableViewArrayListSelectionModel class' selectPrevious() method (line 3034). The line there:

      } else if (pos.getRow() < getRowCount() - 1) {

      should change to:

      } else if (pos.getRow() > 0) {

      to correctly check that the position is not on the first row.
      I suspect this issue resulted from a copy-paste of the selectNext() method.

      FREQUENCY : always


        Attachments

          Activity

            People

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

              Dates

              • Created:
                Updated: