javajavafxjavafx-8javafx-9

JavaFX ComboBox becomes unclickable after removal and re-adding


I think I perhaps have found a bug in Java, or maybe I am doing something wrong.

I populate a container based on some received data. The container has one or more ComboBoxes. On ComboBox selection change I receive new data. I then clear the GridPane and re-add the nodes (that still exist in the new data, and/or add new nodes).

The ComboBox still has focus, but I am unable to activate it again on click. Anything which causes the ComboBox to lose focus (such as focusing another component) will cause it to work again.

This is an simplified example. Tried with jdk1.8.0_162 and jdk-9.0.4

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ComboBoxTest extends Application {

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

    @Override
    public void start(Stage stage) {
        VBox root = new VBox();

        final ComboBox<String> choices = new ComboBox<>();
        choices.getItems().add("Test1");
        choices.getItems().add("Test2");
        root.getChildren().add(choices);

        choices.getSelectionModel().selectedItemProperty().addListener(
            (observable, oldValue, newValue) -> {
                root.getChildren().clear();
                root.getChildren().add(choices);
        });

        Platform.setImplicitExit(true);
        stage.setScene(new Scene(root));
        stage.show();
    }
}

The design is dynamic. I have a list of values received from a server. This is used to create and place ComboBox on a grid. When the user changes a selection in a ComboBox it receive a new list of values from the server. This list may still contain values that corresponds to existing nodes in the grid. They are reused rather than re-created.


Solution

  • Just to not loose reason and solution posted as comment to the deleted answer by sillyfly (post your own and I'll delete this :)

    A little guess as to the underlying cause/issue - the change causes the ComboBox to disappear while its list (which is technically a different stage) is showing. My guess is that leaves it in an indefinite state where it thinks the list is still showing, but it never hides so it doesn't reset. In this case, maybe calling ComboBox::hide will also work

    This assumption is correct as you can see if you change the selection by keyboard (in which case the dropdown is not open): the combo is still accessible by keyboard and mouse. So hiding the dropdown before removing indeed is the solution.

    In code (the simplified example in the Michael's edit)

    public class ReaddFocusedCombo extends Application {
    
        @Override
        public void start(Stage stage) {
            VBox root = new VBox();
    
            final ComboBox<String> choices = new ComboBox<>();
            choices.getItems().add("Test1");
            choices.getItems().add("Test2");
            root.getChildren().add(choices);
    
            choices.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
                // guess by sillyfly: combo gets confused if popup still open 
                choices.hide();
                root.getChildren().clear();
                root.getChildren().add(choices);
                // suggested in answer: working but then the choice isn't focused
                //root.requestFocus();
                // doesn't work
                //  choices.requestFocus();
            });
    
            stage.setScene(new Scene(root));
            stage.show();
        }
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    Update: a little search in the bug parade turned up a similar misbehaviour on adding a showing combo which was fixed on initial attaching to a scene, but missed the dynamic use case. Filed a new issue for the latter.