Skift Wolf Skift Wolf - 1 month ago 5
Java Question

JavaFx Stuck in while loop

I am new to JavaFx and am trying to code my hangman game. I am at the point where I have to check to see if the file that holds my lexicon is there. If not ask user to enter its location. I am trying to do this in a popup windows that asks the user to enter the location and hit a button, This button in turn will cause the textbox.getText() to be saved and loaded in. If it is a bad location, repeat. I have purposely misspelled the location to get this popup to run, but it is stuck in a while loop and forced me to end app. I am running it as a new Scene, but am stuck on what to do next. Here is my Controller.java

FYI: though this is a homework assignment from a university, I am not enrolled. I am on disability and my friend is going through the class. I am trying to stay busy by playing with his assignments. Love to code and this keeps me busy, up-to-date, and I get to learn new things.

package sample;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;


public class Controller {

public Label welcomeLabel, guessesLettersLabel, GuessesRemainingLabel, currentStateLAbel, endGameMessageLabel;
public Button newGameButton, exitButton, enterTextButton;
public TextField enterTextBox;
public boolean ynAnswer;
public String textAnswer;
Stage window;
File inputFile;


public void newGame() throws FileNotFoundException {

Scanner input = new Scanner(System.in);

inputFile = new File("src\\sample\\HangmanLexcon.txt");
while(!inputFile.exists()) {
window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle("File was not found.");
window.setMinWidth(250);
window.setMaxHeight(100);

//form
TextField fileInput = new TextField();
Button inputButton = new Button("Upload File");
inputButton.setOnAction(e -> {
inputFile = new File(fileInput.getText());
window.close();
});


//Layout
VBox layout = new VBox(10);
layout.setPadding(new Insets(20, 20, 20, 20));
layout.getChildren().addAll(fileInput, inputButton);

//Set scene and show in window.
Scene scene = new Scene(layout, 300, 100);
window.setScene(scene);
window.show();

}

Hangman newGame = new Hangman(inputFile.toString());
welcomeLabel.setText("A word has been selected. Good luck, and may the odds ever be in your favor.");

guessesLettersLabel.setText("Guessed Letters: " + newGame.getGuessedLetters());
GuessesRemainingLabel.setText("Guesses remaining: " + newGame.getIncorrectGuessesRemaining());
currentStateLAbel.setText("Current State: " + newGame.getCurrentState());

}

public void enterTextButton(){
System.out.println("You pressed enter...");
}

public void enterText(){
enterTextButton();
}

/**
* If the player closed to exit the game or hit the X button this method will ask if they are
* sure they wish to exit before quiting the game.
*/
public void exitGame(){
Boolean answer = desplayYesNoMessgae("Exit", "Are you sure you want to exit?");
if(answer)
System.exit(0);
}

public boolean desplayYesNoMessgae(String title, String message){
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(title);
window.setMinWidth(250);
Label label1 = new Label();
label1.setText(message);

//Create two buttons, yes and no
Button yesButton = new Button("Yes");
Button noButton = new Button("No");

//If yes ic clucked, set answer to true.
yesButton.setOnAction(e -> {
ynAnswer = true;
window.close();
});

//if no is clicked, set answer to false.
noButton.setOnAction(e -> {
ynAnswer = false;
window.close();
});

VBox layout = new VBox(10);
layout.getChildren().addAll(label1, yesButton, noButton);
layout.setAlignment(Pos.CENTER);
Scene scene = new Scene(layout);
window.setScene(scene);
window.showAndWait();

return ynAnswer;
}

public String desplayTextMessgae(String title, String message){
Stage window = new Stage();
window.initModality(Modality.APPLICATION_MODAL);
window.setTitle(title);
window.setMinWidth(250);
Label label1 = new Label();
label1.setText(message);

//form
TextField nameInput = new TextField();
Button loadBbutton = new Button("Upload");
loadBbutton.setOnAction(e -> {
textAnswer = nameInput.getText();
window.close();
});


//Layout
VBox layout = new VBox(10);
layout.setPadding(new Insets(20, 20, 20, 20));
layout.getChildren().addAll(nameInput, loadBbutton);

//Set scene and show in window.
Scene scene = new Scene(layout, 300, 100);
window.setScene(scene);
window.show();

return textAnswer;
}

}

Answer

In GUI programming, always consider an event-driven approach before considering loop-based approaches. Your while loop simply repeatedly creates controls, registers a listener with one of them (the button), places the controls in a new window, and shows the window. The loop is not going to wait for the button to be pressed before proceeding to the next iteration. So you end up with lots of windows...

You want the code to block when the stage is shown until a valid file is entered. Stage defines a showAndWait method that will show the window and then block execution until the window is dismissed. (Note this is essentially the only blocking method you should call on the FX Application Thread. It does some magic to ensure the UI remains responsive while the code execution is blocked.) So one approach is to call showAndWait in your loop, as this will prevent the loop progressing to the next iteration until the window is dismissed:

while(!inputFile.exists()) {
    window = new Stage();
    window.initModality(Modality.APPLICATION_MODAL);
    window.setTitle("File was not found.");
    window.setMinWidth(250);
    window.setMaxHeight(100);

    //form
    TextField fileInput = new TextField();
    Button inputButton = new Button("Upload File");
    inputButton.setOnAction(e -> {
        inputFile = new File(fileInput.getText());
        window.close();
    });


    //Layout
    VBox layout = new VBox(10);
    layout.setPadding(new Insets(20, 20, 20, 20));
    layout.getChildren().addAll(fileInput, inputButton);

    //Set scene and show in window.
    Scene scene = new Scene(layout, 300, 100);
    window.setScene(scene);
    window.showAndWait();

}

You could make this a bit nicer by only closing the window if the file exists:

inputButton.setOnAction(e -> {
    inputFile = new File(fileInput.getText());
    if (inputFile.exists()) {
        window.close();
    }
});

and you could make it even nicer by disabling the button unless the file exists:

TextField fileInput = new TextField();
Button inputButton = new Button("Upload File");
inputButton.setDisable(true);
fileInput.textProperty().addListener((obs, oldText, newText) -> 
    inputButton.setDisable( ! new File(newText).exists() ) );
inputButton.setOnAction(e -> {
    inputFile = new File(fileInput.getText());
    window.close();
});

or with a binding:

TextField fileInput = new TextField();
Button inputButton = new Button("Upload File");
inputButton.disableProperty().bind(Bindings.createBooleanBinding(
    () -> ! new File(fileInput.getText()).exists(), 
    fileInput.textProperty()));
inputButton.setOnAction(e -> {
    inputFile = new File(fileInput.getText());
    window.close();
});

Finally, with the OK button disabled unless the file is valid, you could make the window undecorated (no minimize, maximize, or, crucially, close buttons). Then it would be impossible to close the window unless a valid file were entered, and you could get rid of the loop entirely. Note this might be annoying to the user if they can't figure out a valid file (there is no escape route):

if (! inputFile.exists()) {
    window = new Stage();
    window.initModality(Modality.APPLICATION_MODAL);
    window.initStyle(StageStyle.UNDECORATED);
    window.setTitle("File was not found.");
    window.setMinWidth(250);
    window.setMaxHeight(100);

    //form
    TextField fileInput = new TextField();
    Button inputButton = new Button("Upload File");
    inputButton.disableProperty().bind(Bindings.createBooleanBinding(
        () -> ! new File(fileInput.getText()).exists(), 
        fileInput.textProperty()));
    inputButton.setOnAction(e -> {
        inputFile = new File(fileInput.getText());
        window.close();
    });


    //Layout
    VBox layout = new VBox(10);
    layout.setPadding(new Insets(20, 20, 20, 20));
    layout.getChildren().addAll(fileInput, inputButton);

    //Set scene and show in window.
    Scene scene = new Scene(layout, 300, 100);
    window.setScene(scene);
    window.showAndWait();
}

And one last comment: to allow the user to choose a file, you might consider using a FileChooser (tutorial).