Search code examples
javafxfxmlactionevent

javaFX show correct value on button coordinates


I've reduced the question to just the problem, so I'm making a UI for minesweeper game, in the controller below, in the StartGame method, things are well (according to my check using debug) until the line of

game.open(x,y)

, meaning I manage to create the board and add buttons (which will act as the slots) to the GridPane, but my issue is that I can't seem to open and show the correct value on the slot. (the logics of Mines class works fine - the one without the javaFX part).

package MS;

import java.io.IOException;
import java.util.Random;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class NewGameCONTROLLER {
    Mines game;
    int rows, columns, mines;
    Button b;

    @FXML
    private TextField NumRows;

    @FXML
    private TextField NumCols;

    @FXML
    private TextField NumM;

    @FXML
    private Button StartGame;

    @FXML
    private Button BackMainMenu;

    @FXML
    private Button RandomGame;

    @FXML
    void BackMainMenu(ActionEvent event) throws IOException {
        FXMLLoader loader = new FXMLLoader(); // Create loading object
        loader.setLocation(getClass().getResource("MainMenuFXML.fxml")); // fxml location
        VBox root = loader.load(); // Load layout
        root.setStyle("-fx-background-image: url(\"file:///C:/EclipseProjects/MineSweeper/src/MS/menu.jpg\")");
        Scene scene = new Scene(root); // Create scene with chosen layout
        Stage primaryStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        primaryStage.setTitle("..."); // Set stage's title
        primaryStage.setMinWidth(400); // Won't be allowed to make width/height smaller
        primaryStage.setMinHeight(350);
        primaryStage.setMaxWidth(600);
        primaryStage.setMaxHeight(450);
        primaryStage.setScene(scene); // Set scene to stage
        primaryStage.show(); // Show stage
    }

    @FXML
    void RandomGame(ActionEvent event) {
        Random rand = new Random();
        int low = 3, high = 15;
        int minesLow = 1, minesHigh;
        rows = rand.nextInt(high - low) + low;
        columns = rand.nextInt(high - low) + low;
        minesHigh = rows * columns - 1;
        mines = rand.nextInt(minesHigh - minesLow) + minesLow;
        game = new Mines(rows, columns, mines);
    }

    @FXML
    void StartGame(ActionEvent event) throws IOException {
        rows = Integer.parseInt(NumRows.getText());
        columns = Integer.parseInt(NumCols.getText());
        mines = Integer.parseInt(NumM.getText());
        game = new Mines(rows, columns, mines);

        FXMLLoader loader = new FXMLLoader(); // Create loading object
        loader.setLocation(getClass().getResource("BoardFXML.fxml")); // fxml location

        AnchorPane root = loader.load(); // Load layout
        root.setStyle("-fx-background-image: url(\"file:///C:/EclipseProjects/MineSweeper/src/MS/Pic.jpg\")");
        Scene scene = new Scene(root); // Create scene with chosen layout
        Stage gameStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
        gameStage.setTitle("..."); // Set stage's title
        gameStage.setMinWidth(1000); // Won't be allowed to make width/height smaller
        gameStage.setMinHeight(500);
        gameStage.setMaxWidth(1200);
        gameStage.setMaxHeight(800);
        gameStage.setScene(scene); // Set scene to stage

        BoardCONTROLLER bCont = loader.getController(); // Prepare board in BoardCONTROLLER
        for (int i = 0; i < columns; i++)
            for (int j = 0; j < rows; j++) {
                b = new Button(" ");
                b.setMinSize(40, 40);
                b.setMaxSize(40, 40);
                bCont.TheBoard.add(b, i, j);
            }
        for (int i = 0; i < bCont.TheBoard.getChildren().size(); i++) {
            ((ButtonBase) bCont.TheBoard.getChildren().get(i)).setOnAction(new EventHandler<ActionEvent>() {

                @Override
                public void handle(ActionEvent event) {
                    int x, y;
                    event.getSource();
                    x = (int) ((Button) event.getSource()).getProperties().get("gridpane-column");
                    y = (int) ((Button) event.getSource()).getProperties().get("gridpane-row");
                    game.open(x, y);
                    for (int i=0;i<game.getCol();i++)
                        for (int j=0;i<game.getRow();j++) {
                            if (game.board[i][j].charAt(1)=='T')
                                ((Button) bCont.TheBoard.getChildren().get(i)).setText(game.get(x, y));
                        }
                /*  while ()
                    for (int i = 0; i < bCont.TheBoard.getChildren().size(); i++) {
                        if (game.board[x][y].charAt(2) == 'B') {
                            ((Button) bCont.TheBoard.getChildren().get(i)).setText(game.get(x, y));
                        }
                    }*/
                }
            });
        }
        gameStage.show(); // Show stage
    }

}

Thank you.


Solution

  • When I look at this loop.

    for (int i=0;i<game.getCol();i++)
        for (int j=0;i<game.getRow();j++) {
            if (game.board[i][j].charAt(1)=='T')
                ((Button) bCont.TheBoard.getChildren().get(i)).setText(game.get(x, y));
        }    
    }
    

    The variable i does not correspond to a row coordinate, it corresponds to the n'th child in the board. So really, your just looping through the all of the children in the first row (or first column).

    On way I can see to fix that;

    for( Node child: bCont.TheBoard.getChildren() ){
        int i = (int) ((Button) child).getProperties().get("gridpane-column");
        int j = (int) ((Button) child).getProperties().get("gridpane-row");
        if (game.board[i][j].charAt(1)=='T'){
            ((Button) child).setText(game.get(x, y));
        }
    }
    

    That way you go through all the children and check their status on the board.

    I don't know if the order of the children nodes in GridPane correspond to the layout order, a common technique to pack a 2D array into a 1D array,

    bCont.TheBoard.getChildren().get(i + j*game.getCol());
    

    Remember you have row x col total buttons.