Search code examples
javajavafxsudokugridpane

JavaFX - Clicking inside GridPane prints out Pane instead of TextField


I'm trying to make a Sudoku Game in JavaFX. I made the 9x9 grid using GridPane and TextField.

Now I want to change the background color of the TextField when user clicks inside it. To check that everyting is fine I am prining the target od the MouseEvent.

My problem is that when I click in the center of TextField, the target is Pane and when i I click elsewhere the target is my GridPane and the background color is changing.

What should I do? I can't figure out how to do it!

public class SudokuGrid {

public static final int GRID_SIZE = 9;

private TextField[][] sudokuCells;
private GridPane sudokuGrid;

public SudokuGrid () {
    sudokuCells = new TextField[GRID_SIZE][GRID_SIZE];
    createSudokuGrid();
    for (int row = 0; row < GRID_SIZE; row++) {
        for(int col = 0; col < GRID_SIZE; col++) {
            sudokuCells[row][col] = new TextField() {
                  @Override
                    public void replaceText(int start, int end, String text) {
                        // If the replaced text would end up being invalid, then simply
                        // ignore this call!
                        if (text.matches("[1-9]|\\s")) {
                            super.setText(text);
                        }
                    }
                };
            sudokuCells[row][col].setPrefSize(60, 60);
            sudokuCells[row][col].setStyle("-fx-background-color: yellow;");
            sudokuGrid.add(sudokuCells[row][col], col, row);
            sudokuGrid.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent e) {
                    Object source = e.getTarget();
                    System.out.println(source);
                    if(source instanceof TextField) {
                        ((TextField) source).setStyle("-fx-background-color: green;");
                    }
                }
                });
        }
    }   
    sudokuGrid.setPrefSize(270, 270); // 30 * 9
    sudokuGrid.setGridLinesVisible(true);
}

private void createSudokuGrid() {
    sudokuGrid = new GridPane();
    for (int i = 0; i  < GRID_SIZE; i++) {
        RowConstraints rc = new RowConstraints();
        rc.setVgrow(Priority.ALWAYS) ; // allow row to grow
        rc.setFillHeight(true); // ask nodes to fill height for row
        // other settings as needed...
        sudokuGrid.getRowConstraints().add(rc);

        ColumnConstraints cc = new ColumnConstraints();
        cc.setHgrow(Priority.ALWAYS) ; // allow column to grow
        cc.setFillWidth(true); // ask nodes to fill space for column
        // other settings as needed...
        sudokuGrid.getColumnConstraints().add(cc);
    }

}

Solution

  • The source of the event is the object on which you set the event filter; i.e. in this case it is sudokuGrid. So the condition

    if (source instanceof TextField)
    

    in your handler will never be true, since the only possible source is the sudokuGrid.

    If you want to change the background color of the text field, you can add the event filter to the text field itself:

    TextField sudokuCell = sudokuCells[row][col];
    sudokuCell.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> 
        sudokuCell.setStyle("-fx-background-color: green;"));
    

    Better still would be to respond to changes in the text field's focused property (because using a mouse listener will not change the background if the user uses the Tab key to navigate to different text fields):

    TextField sudokuCell = sudokuCells[row][col];
    sudokuCell.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
        if (isNowFocused) {
            sudokuCell.setStyle("-fx-background-color: green;");
        } else {
            sudokuCell.setStyle("");
        }
    });
    

    And even better would just be to use an external css file to do this:

    sudoku-grid.css:

    .text-field:focused {
        -fx-background-color: green ;
    }
    

    and then in your Java code associate the CSS file with the grid:

    sudokuGrid.getStyleSheets().add("sudoku-grid.css");
    

    and remove the handlers entirely.