Search code examples
javajavafxhbox

Can't click on buttons in HBox


I can't get the buttons in the buttonContainer HBox to work, as in I can't click on them at all, they are "stuck" so to speak. This happens in multiple IDEs so it can't be that. I don't understand why they are stuck without the ability to click on them, but I'm sure I'm making some kind of basic mistake.

Here's my simple code:

public class Game extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    Button attack, run, drinkPotion, nextRoom;
    StackPane root;
    HBox buttonContainer, roomContainer;
    Scene scene;

    public void createNode() {
        root = new StackPane();
        scene = new Scene(root, 860, 640);

        attack = new Button("Attack");
        run = new Button("Run!");
        drinkPotion = new Button("Drink Potion!");
        nextRoom = new Button("Go to next room...");

        buttonContainer = new HBox(12);
        buttonContainer.getChildren().addAll(attack, run, drinkPotion);
        buttonContainer.setAlignment(Pos.BOTTOM_LEFT);

        roomContainer = new HBox();
        roomContainer.getChildren().addAll(nextRoom);
        roomContainer.setAlignment(Pos.BOTTOM_RIGHT);

        root.getChildren().addAll(buttonContainer, roomContainer);
    }

    public void start(Stage stage) {
        createNode();
        stage.setScene(scene);
        stage.show();
    }
} 

Solution

  • In a StackPane, the HBoxes are going to be resized to fill the StackPane. So both HBoxes here are filling the whole area, with the alignment settings positioning the buttons inside them. Thus while the buttons don't actually overlap, the HBoxes do.

    The z-order of nodes in a StackPane (and other panes, in some circumstances) is determined by the order of the nodes in the children list. So in your code, buttonContainer is "behind" roomContainer. This means mouse clicks are targeted either to the buttons in roomContainer, or to roomContainer itself. Consequently, the buttons in buttonContainer never receive a mouse click.

    A solution to this is to use a "real" layout container to position the two button containers appropriately. For example, you could use an AnchorPane:

    import javafx.application.Application;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    
    public class Game extends Application {
        public static void main(String[] args) {
            launch(args);
        }
    
        Button attack, run, drinkPotion, nextRoom;
        AnchorPane root;
        HBox buttonContainer, roomContainer;
        Scene scene;
    
        public void createNode() {
            root = new AnchorPane();
            scene = new Scene(root, 860, 640);
    
            attack = new Button("Attack");
            run = new Button("Run!");
            drinkPotion = new Button("Drink Potion!");
            nextRoom = new Button("Go to next room...");
    
            buttonContainer = new HBox(12);
            buttonContainer.getChildren().addAll(attack, run, drinkPotion);
    
            roomContainer = new HBox();
            roomContainer.getChildren().addAll(nextRoom);
    
            AnchorPane.setBottomAnchor(buttonContainer, 0.0);
            AnchorPane.setLeftAnchor(buttonContainer, 0.0);
    
            AnchorPane.setBottomAnchor(roomContainer, 0.0);
            AnchorPane.setRightAnchor(roomContainer, 0.0);
    
            root.getChildren().addAll(roomContainer, buttonContainer);
        }
    
        @Override
        public void start(Stage stage) {
            createNode();
            stage.setScene(scene);
            stage.show();
        }
    }
    

    or possibly a BorderPane:

    import javafx.application.Application;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.HBox;
    import javafx.stage.Stage;
    
    public class Game extends Application {
        public static void main(String[] args) {
            launch(args);
        }
    
        Button attack, run, drinkPotion, nextRoom;
        BorderPane root;
        HBox buttonContainer, roomContainer;
        Scene scene;
    
        public void createNode() {
            root = new BorderPane();
            scene = new Scene(root, 860, 640);
    
            attack = new Button("Attack");
            run = new Button("Run!");
            drinkPotion = new Button("Drink Potion!");
            nextRoom = new Button("Go to next room...");
    
            buttonContainer = new HBox(12);
            buttonContainer.getChildren().addAll(attack, run, drinkPotion);
            buttonContainer.setAlignment(Pos.BOTTOM_CENTER);
    
    
            roomContainer = new HBox();
            roomContainer.getChildren().addAll(nextRoom);
            roomContainer.setAlignment(Pos.BOTTOM_CENTER);
    
    
            root.setLeft(buttonContainer);
            root.setRight(roomContainer);
        }
    
        @Override
        public void start(Stage stage) {
            createNode();
            stage.setScene(scene);
            stage.show();
        }
    }
    

    You can read an overview of how all the layout panes work in the tutorial