ADDITIONAL SYSTEM INFORMATION :
JavaFX version 15 and 16
A DESCRIPTION OF THE PROBLEM :
index computation and rendering of combobox has issues when removing multiple items at once with itemsProperty().get().removeAll(Collection). This is because of a difference in how the list change is computed and how the selected index is updated.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Precondition:
- combobox with three items
- middle item selected
Action:
- remove first and last item via combox.itemsProperty().get().removeAll(...) (in example: button "Trigger bug")
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
middle item stays selected and rendered, selected index is 0 (first and only item)
ACTUAL -
middle item stays selected, but rendered text is empty, since selected index is -1.
---------- BEGIN SOURCE ----------
package test;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class App extends Application {
private ComboBox<String> comboBox;
@Override
public void start(Stage primaryStage) throws Exception {
var root = new StackPane();
var scene = new Scene(root);
primaryStage.setScene(scene);
var box = new VBox();
var buttonBug = new Button();
var buttonWorkaround = new Button();
var buttonReset = new Button();
comboBox = new ComboBox<>();
comboBox.itemsProperty().set(FXCollections.observableArrayList(List.of("foo", "bar", "baz")));
comboBox.valueProperty().set("bar");
buttonBug.textProperty().set("Trigger bug");
buttonBug.onActionProperty().set(e -> comboBox.itemsProperty().get().removeAll(getAllButSelected()));
buttonWorkaround.textProperty().set("Workaround bug");
buttonWorkaround.onActionProperty().set(e -> getAllButSelected().forEach(comboBox.itemsProperty().get()::remove));
buttonReset.textProperty().set("Reset");
buttonReset.onActionProperty().set(e -> {
comboBox.itemsProperty().get().setAll("foo", "bar", "baz");
comboBox.valueProperty().set("bar");
});
box.getChildren().addAll(comboBox, buttonBug, buttonWorkaround, buttonReset);
root.getChildren().add(box);
primaryStage.show();
}
private Set<String> getAllButSelected() {
var result = new HashSet<>(comboBox.itemsProperty().get());
var selected = comboBox.valueProperty().get();
result.remove(selected);
return result;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
remove items one by one instead of with removeAll (button "Workaround bug")
FREQUENCY : always
JavaFX version 15 and 16
A DESCRIPTION OF THE PROBLEM :
index computation and rendering of combobox has issues when removing multiple items at once with itemsProperty().get().removeAll(Collection). This is because of a difference in how the list change is computed and how the selected index is updated.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Precondition:
- combobox with three items
- middle item selected
Action:
- remove first and last item via combox.itemsProperty().get().removeAll(...) (in example: button "Trigger bug")
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
middle item stays selected and rendered, selected index is 0 (first and only item)
ACTUAL -
middle item stays selected, but rendered text is empty, since selected index is -1.
---------- BEGIN SOURCE ----------
package test;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class App extends Application {
private ComboBox<String> comboBox;
@Override
public void start(Stage primaryStage) throws Exception {
var root = new StackPane();
var scene = new Scene(root);
primaryStage.setScene(scene);
var box = new VBox();
var buttonBug = new Button();
var buttonWorkaround = new Button();
var buttonReset = new Button();
comboBox = new ComboBox<>();
comboBox.itemsProperty().set(FXCollections.observableArrayList(List.of("foo", "bar", "baz")));
comboBox.valueProperty().set("bar");
buttonBug.textProperty().set("Trigger bug");
buttonBug.onActionProperty().set(e -> comboBox.itemsProperty().get().removeAll(getAllButSelected()));
buttonWorkaround.textProperty().set("Workaround bug");
buttonWorkaround.onActionProperty().set(e -> getAllButSelected().forEach(comboBox.itemsProperty().get()::remove));
buttonReset.textProperty().set("Reset");
buttonReset.onActionProperty().set(e -> {
comboBox.itemsProperty().get().setAll("foo", "bar", "baz");
comboBox.valueProperty().set("bar");
});
box.getChildren().addAll(comboBox, buttonBug, buttonWorkaround, buttonReset);
root.getChildren().add(box);
primaryStage.show();
}
private Set<String> getAllButSelected() {
var result = new HashSet<>(comboBox.itemsProperty().get());
var selected = comboBox.valueProperty().get();
result.remove(selected);
return result;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
remove items one by one instead of with removeAll (button "Workaround bug")
FREQUENCY : always