-
Bug
-
Resolution: Fixed
-
P4
-
jfx14
To reproduce, compile and run the example below
- see the first item shown (and its toggle selected on opening the popup)
- click button to select an uncontained item
- expected and actual: value and selected item are sync'ed (verified by print out)
[ ignoreJDK-8087555:
- expected uncontained items displayed
- actual: old selected item displayed
]
- open popup
- expected: no toggle selected
- actual: old toggle selected
Reason is a broken invariant (also partly noted - kind-of, a bit upside down - in JDK-8088012) in the implementation of choiceBox' selectionModel (maybe even in its super SingleSelectionModel?):
assertEquals(getItems().indexOf(selectedItem), getSelectedIndex())
Fix in ChoiceBoxSelectionModel, override select(item) to explicitly set selectedIndex to -1 if not contained:
@Override
public void select(T obj) {
super.select(obj);
// super doesn't updatee selectedIndex for uncontained selectedItem
// if we don't add this, the incorrect radioItem is marked selected
// with uncontained selectedItem (after there had been a contained once)
if (obj != null && !choiceBox.getItems().contains(obj)) {
setSelectedIndex(-1);
}
}
It might be cleaner to fix this in SingleSelectionModel (at least in the long run). But ComboBox has a super constraint violation specified as intended behaviour (aside: I think that's utterly impossible!), better not touch super for now.
The boundary between this issue andJDK-8087555 is that this is a failure in the selectionModel while the other is a failure in the skin
The example:
public class ChoiceBoxSelectedToggleBug extends Application {
ObservableList<String> items = FXCollections.observableArrayList(
"5-item", "4-item", "3-item", "2-item", "1-item");
private String title;
private Parent getContent() {
ChoiceBox<String> box = new ChoiceBox<>(items);
box.setValue(box.getItems().get(0));
Button setValue = new Button("Set value to uncontained");
setValue.setOnAction(e -> {
box.setValue("myDummyValue");
System.out.println("value/ selected item: " + box.getValue() +
box.getSelectionModel().getSelectedItem());
});
BorderPane pane = new BorderPane(box);
pane.setBottom(new HBox(10, setValue));
title = box.getClass().getSimpleName();
return pane;
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(getContent());
primaryStage.setScene(scene);
primaryStage.setTitle(System.getProperty("java.version") + title);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
- see the first item shown (and its toggle selected on opening the popup)
- click button to select an uncontained item
- expected and actual: value and selected item are sync'ed (verified by print out)
[ ignore
- expected uncontained items displayed
- actual: old selected item displayed
]
- open popup
- expected: no toggle selected
- actual: old toggle selected
Reason is a broken invariant (also partly noted - kind-of, a bit upside down - in JDK-8088012) in the implementation of choiceBox' selectionModel (maybe even in its super SingleSelectionModel?):
assertEquals(getItems().indexOf(selectedItem), getSelectedIndex())
Fix in ChoiceBoxSelectionModel, override select(item) to explicitly set selectedIndex to -1 if not contained:
@Override
public void select(T obj) {
super.select(obj);
// super doesn't updatee selectedIndex for uncontained selectedItem
// if we don't add this, the incorrect radioItem is marked selected
// with uncontained selectedItem (after there had been a contained once)
if (obj != null && !choiceBox.getItems().contains(obj)) {
setSelectedIndex(-1);
}
}
It might be cleaner to fix this in SingleSelectionModel (at least in the long run). But ComboBox has a super constraint violation specified as intended behaviour (aside: I think that's utterly impossible!), better not touch super for now.
The boundary between this issue and
The example:
public class ChoiceBoxSelectedToggleBug extends Application {
ObservableList<String> items = FXCollections.observableArrayList(
"5-item", "4-item", "3-item", "2-item", "1-item");
private String title;
private Parent getContent() {
ChoiceBox<String> box = new ChoiceBox<>(items);
box.setValue(box.getItems().get(0));
Button setValue = new Button("Set value to uncontained");
setValue.setOnAction(e -> {
box.setValue("myDummyValue");
System.out.println("value/ selected item: " + box.getValue() +
box.getSelectionModel().getSelectedItem());
});
BorderPane pane = new BorderPane(box);
pane.setBottom(new HBox(10, setValue));
title = box.getClass().getSimpleName();
return pane;
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(getContent());
primaryStage.setScene(scene);
primaryStage.setTitle(System.getProperty("java.version") + title);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
- relates to
-
JDK-8088012 SelectionModel: mismatches between api doc and implementations
- Open
-
JDK-8087555 [ChoiceBox] uncontained value not shown
- Resolved
-
JDK-8242489 ChoiceBox: initially toggle not sync'ed to selection
- Resolved