3

I have a TextField and a ListView. As the user types in the TextField, suggestions come up in the ListView:

enter image description here

When the TextField is empty, the ListView disappears, by setting the visible and managed properties to false.

However, when the user starts to type, the ListView takes up space and pushes everything down. Using .setManaged(false) allows it not to take up any space, but it doesn't display anymore, as I haven't defined a position for it. I have tried setting the layoutX and layoutY of the search list, but it still doesn't display.

Ideally I'd like the ListView's position to be affected by the layout but not to take up any space.

Any ideas?

Norbo11
  • 143
  • 1
  • 11
  • Related: [JavaFX 2 custom popup pane](http://stackoverflow.com/questions/12717969/javafx-2-custom-popup-pane). That answer might be a bit dated and there may be superior ways to accomplish this behavior in JavaFX 8+. – jewelsea Feb 27 '15 at 19:12
  • I have the same problem and tried the same approach as you did. Have you solved the problem that the list is invisible when managed is set to false? – T3rm1 Oct 04 '15 at 21:13

1 Answers1

0

Wrap the container that holds the text field(s) in an AnchorPane. Add the ListView to the AnchorPane after the text field container (so it stays on top). Then you need to position the ListView appropriately relative to the text field when you make it visible; I think the best way to do this is to first convert the bounds of the text field from local coordinates to Scene coordinates, then convert those bounds to the coordinates relative to the AnchorPane.

Here's an SSCCE:

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class SuggestionList extends Application {

    @Override
    public void start(Stage primaryStage) {
        AnchorPane root = new AnchorPane();

        ListView<String> suggestionBox = new ListView<>();
        suggestionBox.getItems().addAll("Here", "Are", "Some", "Suggestions");
        suggestionBox.setMaxHeight(100);
        suggestionBox.setVisible(false);

        // Grid pane to hold a bunch of text fields:
        GridPane form = new GridPane();
        for (int i=0; i<10; i++) {
            form.addRow(i, new Label("Enter Text:"), createTextField(suggestionBox));
        }

        // just move the grid pane a little to test suggestion box positioning:
        AnchorPane.setLeftAnchor(form, 20.0);
        AnchorPane.setRightAnchor(form, 20.0);
        AnchorPane.setTopAnchor(form, 20.0);
        AnchorPane.setBottomAnchor(form, 20.0);

        // allows focus on grid pane, so user can click on it to remove focus from text field.
        form.setFocusTraversable(true);

        root.setPadding(new Insets(20));

        root.getChildren().addAll(form, suggestionBox);

        Scene scene = new Scene(root, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private TextField createTextField(ListView<String> suggestionBox) {
        TextField textField = new TextField();
        ChangeListener<String> selectionListener = (obs, oldItem, newItem) -> {
            if (newItem != null) {
                textField.setText(newItem);
            }
        };
        textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
            if (isNowFocused) {
                suggestionBox.setVisible(true);

                // compute bounds of text field relative to suggestion box's parent:
                Parent parent = suggestionBox.getParent(); // (actually the anchor pane)
                Bounds tfBounds = textField.getBoundsInLocal();
                Bounds tfBoundsInScene = textField.localToScene(tfBounds);
                Bounds tfBoundsInParent = parent.sceneToLocal(tfBoundsInScene);

                // position suggestion box:
                suggestionBox.setLayoutX(tfBoundsInParent.getMinX());
                suggestionBox.setLayoutY(tfBoundsInParent.getMaxY());
                suggestionBox.setPrefWidth(tfBoundsInParent.getWidth());

                suggestionBox.getSelectionModel().selectedItemProperty().addListener(selectionListener);
            } else {
                suggestionBox.setVisible(false);
                suggestionBox.getSelectionModel().selectedItemProperty().removeListener(selectionListener);
            }
        });
        textField.setOnAction(event -> {
            suggestionBox.setVisible(false);
            suggestionBox.getSelectionModel().selectedItemProperty().removeListener(selectionListener);         
        });
        return textField ;
    }

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

You might be able to use similar positional tricks and just add it to the same scene, with managed set to false.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • Thanks, this is exactly what I need, but I can't seem to get it working with my program. What is the exact purpose of the AnchorPane? Couldn't I just use a VBox, for instance? Your code doesn't exactly fit my scenario as I am displaying a different suggestion box for every text field. – Norbo11 Mar 01 '15 at 19:56
  • A VBox won't work as it positions it's child nodes so the appear one vertically above the other. You need to use a Pane that allows you to position the child nodes yourself, which is why I used an AnchorPane. You can easily adapt the code so it creates a different suggestion box for each text field. – James_D Mar 01 '15 at 20:04
  • I have managed to get nearly get it to work.. but it is currently displaying behind the table which is right underneath the TextField: http://i.imgur.com/Jpc0tLQ.png. The FXML which generates this is as follows: http://pastie.org/9992170. Does the TableView have to be a sibling of the ListView for this to work? I also managed to get it to this stage without using an AnchorPane, so I'm still not entirely sure about that. – Norbo11 Mar 01 '15 at 21:13