Suemayah Eldursi
Suemayah Eldursi

Reputation: 1009

Add invisible row in javafx gridpane

I would like to add an invisible row in a gridpane. Then I would like to show and hide it based on user selection. I've managed to get the general functionality working but the problem is when the row content are hidden there is an extra space. I am not sure if this is the actual row or the vgap? Anyway I would like to get rid of this extra space. Here is my gridpane :

public class CustomGrid extends GridPane{

public CustomGrid() {
    this.setHgap(20);
    this.setVgap(20);

    ColumnConstraints firstColConstraints =  new ColumnConstraints();
    firstColConstraints.setHalignment(HPos.RIGHT);
    firstColConstraints.setPercentWidth(50.0);
    firstColConstraints.setHgrow(Priority.ALWAYS);
    ColumnConstraints secondColConstraints =  new ColumnConstraints();
    ColumnConstraints thirdColConstraints =  new ColumnConstraints();
    thirdColConstraints.setHgrow(Priority.ALWAYS);
    thirdColConstraints.setHalignment(HPos.LEFT);

    this.getColumnConstraints().addAll(firstColConstraints, secondColConstraints, thirdColConstraints);
    VBox.setMargin(this, new Insets(10, 0, 50, 0));

}

I've added content then hidden it like so

Pane content = new Pane()
pane.setVisible(false);
pane.setManaged(false);
add(content, 0, 1);

I'm happy with it when the user makes it visible but when it is hidden there is an extra gap between the rows. Can anyone tell me a way to fix that.

Thank you.

Upvotes: 0

Views: 6245

Answers (2)

jewelsea
jewelsea

Reputation: 159416

I think the best way to handle this is to actually remove the row from the grid pane rather than trying to make the row "invisible". Inserting and removing rows at arbitrary locations in a GridPane is a little tricky as the GridPane API does not have native support of such functions. So I created a little utility based upon your custom GridPane implementation which demonstrates adding and removing rows within the GridPane. To do so, the custom implementation has to keep an internal notion of which nodes are located at which rows and to update the column constraints of each of the nodes and each of the rows whenever new rows are added or deleted. Hiding can be accomplished by removing a row from the grid pane and then, at a later stage, adding the same row back into the grid pane at the index from which it was earlier removed. I understand this isn't exactly what you were looking for, but it seemed the most reasonable solution to me.

append

/** Click on a Label to remove the row containing the label from the GridPane */
import javafx.application.Application;
import javafx.collections.*;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.*;

public class HiddenNodes extends Application {

    private static final int N_COLS = 3;
    private static final int INIT_N_ROWS = 10;

    private int nextRowId = 0;
    private Node[] lastRemovedRow = null;

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

    @Override
    public void start(Stage stage) throws Exception {
        CustomGrid grid = new CustomGrid();

        for (int i = 0; i < INIT_N_ROWS; i++) {
            Label[] labels = createRow(grid);
            grid.insertRow(i, labels);
        }

        Button prepend = new Button("Prepend");
        prepend.setOnAction(event -> {
            Label[] labels = createRow(grid);
            grid.insertRow(0, labels);
        });

        Button append = new Button("Append");
        append.setOnAction(event -> {
            Label[] labels = createRow(grid);
            grid.insertRow(grid.getRowsSize(), labels);
        });

        HBox controls = new HBox(10, prepend, append);

        VBox layout = new VBox(10, controls, grid);
        layout.setPadding(new Insets(10));

        stage.setScene(new Scene(layout, 200, 600));
        stage.show();
    }

    private Label[] createRow(CustomGrid grid) {
        Label[] labels = new Label[N_COLS];
        for (int j = 0; j < N_COLS; j++) {
            final int fj = j;
            labels[j] = new Label(nextRowId + ":" + j);
            labels[j].setStyle("-fx-background-color: coral;");
            labels[j].setOnMouseClicked((MouseEvent e) -> {
                lastRemovedRow = grid.removeRowContaining(labels[fj]);
            });
        }

        nextRowId++;

        return labels;
    }

    class CustomGrid extends GridPane {
        private ObservableList<Node[]> rows = FXCollections.observableArrayList();

        CustomGrid() {
            this.setHgap(20);
            this.setVgap(20);

            ColumnConstraints firstColConstraints = new ColumnConstraints();
            firstColConstraints.setHalignment(HPos.RIGHT);
            firstColConstraints.setPercentWidth(50.0);
            firstColConstraints.setHgrow(Priority.ALWAYS);
            ColumnConstraints secondColConstraints = new ColumnConstraints();
            ColumnConstraints thirdColConstraints = new ColumnConstraints();
            thirdColConstraints.setHgrow(Priority.ALWAYS);
            thirdColConstraints.setHalignment(HPos.LEFT);

            this.getColumnConstraints().addAll(firstColConstraints, secondColConstraints, thirdColConstraints);
            VBox.setMargin(this, new Insets(10, 0, 50, 0));
        }

        void insertRow(int rowIdx, Node... nodes) {
            for (int i = rows.size() - 1; i >= rowIdx; i--) {
                for (int j = 0; j < rows.get(i).length; j++) {
                    setConstraints(rows.get(i)[j], j, i + 1);
                }
            }

            addRow(rowIdx, nodes);
            rows.add(rowIdx, nodes);
        }

        private Node[] removeRow(int rowIdx) {
            for (int i = rows.size() - 1; i > rowIdx; i--) {
                for (int j = 0; j < rows.get(i).length; j++) {
                    setConstraints(rows.get(i)[j], j, i - 1);
                }
            }

            for (Node node: rows.get(rowIdx)) {
                getChildren().remove(node);
            }
            return rows.remove(rowIdx);
        }

        Node[] removeRowContaining(Node node) {
            Iterator<Node[]> rowIterator = rows.iterator();
            int rowIdx = 0;
            while (rowIterator.hasNext()) {
                Node[] row = rowIterator.next();
                for (Node searchNode : row) {
                    if (searchNode == node) {
                        return removeRow(rowIdx);
                    }
                }

                rowIdx++;
            }

            return null;
        }

        int getRowsSize() {
            return rows.size();
        }
    }
}

The above program is just an outline, additional logic would be required to reshow the "hidden" node (which has been removed from the grid) at the appropriate row index (which is a reasonably tricky problem).

Note: You might consider using a TableView rather than a GridPane. By manipulating the item list backing the TableView it is quite trivial to add and remove nodes to the TableView. Of course, I understand a TableView is not the appropriate solution for all problems of this kind (as the TableView comes with a bunch of other functionality, like headers and sorting, which you may not wish to have).

Upvotes: 1

PendingValue
PendingValue

Reputation: 250

As mentioned in the comment (link to comment):

Change the height to 0 when hidden.

Upvotes: 1

Related Questions