-
Bug
-
Resolution: Unresolved
-
P3
-
jfx13, 8u231
-
x86_64
-
linux_ubuntu
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
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