How To Align Ok Button Of A Dialog Pane In Javafx?
Asked Answered
I

6

7

I want to align i.e Position CENTER an OK button of a DialogPane. I have tried the below code but its not working.

Dialog dialog = new Dialog();
DialogPane dialogPane = dialog.getDialogPane();
dialogPane.setStyle("-fx-background-color: #fff;");

        // Set the button types.
ButtonType okButtonType = new ButtonType("Ok", ButtonBar.ButtonData.OK_DONE);
ButtonType cancelButtonType = new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().addAll(okButtonType, cancelButtonType);
dialogPane.lookupButton(cancelButtonType).setVisible(false);

        // Testing
Button okButton = (Button) dialog.getDialogPane().lookupButton(okButtonType);
okButton.setAlignment(Pos.CENTER);
        // End Testing

dialog.showAndWait();
Intersection answered 15/3, 2016 at 11:18 Comment(2)
Ok button is visible at bottom right of dialog, which is its default feature, but I want it to be shown at bottom center of dialog.Intersection
Please use the edit link to add the extra information to the question. Thanks! (And please make the code a complete program - that helps people reproduce what you did, and provide you with a good answer). See help center for how to write a minimal reproducible example.Rabin
Y
5

Centering buttons in the ButtonBar of a Dialog is actually surprisingly difficult to achieve in a non-hacky way.

Below is the best solution I could come up with. It relies upon a dynamic CSS lookup of the HBox for the button container, to which it then adds a spacer region on the right to push the buttons to the left (the default ButtonSkin implementation already places an implicit spacer of the left which pushes the buttons to the right, which I determined using ScenicView). The combination of the left and right spacers end up aligning the buttons in the center. The solution also overrides the ButtonBar creation to stop the ButtonSkin internally reordering and performing additional layout of buttons, as, when it does that, you can't really reliably customize the layout yourself.

centered button

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.Optional;

public class CenteredDialogButtons extends Application {
    @Override
    public void start(Stage stage) {
        Button show = new Button("Show Dialog");

        Dialog<ButtonType> dialog = new Dialog<>();
        DialogPane dialogPane = new DialogPane() {
            @Override
            protected Node createButtonBar() {
                ButtonBar buttonBar = (ButtonBar) super.createButtonBar();
                buttonBar.setButtonOrder(ButtonBar.BUTTON_ORDER_NONE);

                return buttonBar;
            }
        };
        dialog.setDialogPane(dialogPane);
        dialogPane.getButtonTypes().addAll(ButtonType.OK);
        dialogPane.setContentText("Centered Button");

        Region spacer = new Region();
        ButtonBar.setButtonData(spacer, ButtonBar.ButtonData.BIG_GAP);
        HBox.setHgrow(spacer, Priority.ALWAYS);
        dialogPane.applyCss();
        HBox hbox = (HBox) dialogPane.lookup(".container");
        hbox.getChildren().add(spacer);

        show.setOnAction(e -> {
            Optional<ButtonType> result = dialog.showAndWait();
            if (result.isPresent() && result.get() == ButtonType.OK) {
                System.out.println("OK");
            }
        });

        StackPane layout = new StackPane(
                show
        );
        layout.setPadding(new Insets(50));

        Scene scene = new Scene(layout);

        stage.setScene(scene);
        stage.show();
    }

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

The reason I don't quite like this solution is that the dynamic CSS lookups kind of violate API encapsulation, as the CSS structure of the JavaFX scene graphs for controls such as button bars is not really part of their public API. However, I don't think it is really possible to get centered buttons in a ButtonBar using the existing public APIs for JavaFX 8 and a default ButtonBar skin.

An alternate approach would be to create a custom skin for the ButtonBar associated with the dialog, but that approach is quite difficult and I wouldn't recommend it for this task.

Basically, the takeaway from all this is, just leave the default button layout and order for dialogs whenever you can, rather than trying to customize the dialog button layout. If you do want to have completely customized layout to the level of things like button placement, then you may be better off just creating your own custom dialog class by subclassing Stage rather than basing your custom dialog implementation on the in-built dialog class.

Related, but slightly different information is in:

Yippie answered 16/3, 2016 at 0:18 Comment(0)
E
5

I tried to center OK button in Alert and I am not sure if this is bug or feature (Java8) but it was possible to center single button by setting new one:

alert.getButtonTypes().set(0, new ButtonType("OK", ButtonBar.ButtonData.LEFT));

As long as there is only one button with ButtonData.LEFT, it is centered in the middle of button panel. Obviously this solution does not work for panel with multiple buttons, but it might help to position single OK button.

Extortionary answered 10/5, 2019 at 14:27 Comment(1)
thats really weird, cause Lorenzos solution worked fine for 2 buttons, but not for three, so i did this exact thing after adding the buttons, and now all 3 are centered edit: however, as soon as I change the button data, they re arrange and are off centerProtoactinium
T
3

Add this method to your code and call it when you need to align the buttons in a Dialog or Alert:

    private void centerButtons(DialogPane dialogPane) {
      Region spacer = new Region();
      ButtonBar.setButtonData(spacer, ButtonBar.ButtonData.BIG_GAP);
      HBox.setHgrow(spacer, Priority.ALWAYS);
      dialogPane.applyCss();
      HBox hboxDialogPane = (HBox) dialogPane.lookup(".container");
      hboxDialogPane.getChildren().add(spacer);
   }

Call it in this way: centerButtons(dialog.getDialogPane);

Trever answered 28/5, 2019 at 10:59 Comment(1)
this does not work for more than 2 buttons?Protoactinium
C
1

It's a kind of hack, but you could just do something like this:

okButton.translateXProperty().bind(okButton.prefWidthProperty().divide(-2));

The DialogPane is horizontal centered, so subtracting the okButton's half width will do the trick. But I think this is a really dirty solution ;-)

Chromosome answered 15/3, 2016 at 22:1 Comment(0)
B
1

Based on @ManuelSeiche's answer, here is how to compute exact distance to the center:

@FXML private Dialog<ButtonType> dialog;
@FXML private ButtonType btClose;

@FXML
private void initialize()
{
    dialog.setOnShown(event ->
    {
        Platform.runLater(() ->
        {
            Button btnClose = (Button) dialog.getDialogPane().lookupButton(btClose);
            HBox hBox = (HBox) btnClose.getParent();
            double translateAmount = hBox.getWidth() / 2.0 - btnClose.getWidth() / 2.0 - hBox.getPadding().getLeft();
            btnClose.translateXProperty().set(-translateAmount);
        });
    });
}
Baptize answered 8/7, 2018 at 7:19 Comment(0)
F
1

Centering a button(s) is actually quite easy (nonetheless it could be simpler and better documented). It all comes down to “magical” buttonOrder string passed to ButtonBar's setButonOrder(String) method.

But first, the essential code snippet:

@Override public void start(final Stage stage) throws Exception {
    // …
    var button = new ButtonType("Roger!", ButtonBar.ButtonData.OK_DONE);
    var dialog = new Dialog<>();
    dialog.setDialogPane(new DialogPane() {
        @Override protected Node createButtonBar() {
            var buttonBar = (ButtonBar)super.createButtonBar();
            buttonBar.setButtonOrder("L_O_R");
            return buttonBar;
        }
    });
    dialog.getButtonTypes.addAll(button);
    var pane = dialog.getDialogPane();
    // Add whatever is needed to the pane here…
    // …
    dialog.showAndWait();
}

Explanation: the wrapping start(Stage) method is of course Application's start method. First I create a button of OK/DONE type and then a Dialog instance. Nothing unusual so far.

And now: normally, after dialog gets created, we just add buttons and the content to dialog's pane. But here I actually create and set the pane first and a slightly modified one.

As you can see, I create anonymous class extending DialogPane and immediately set it as dialog's pane. What needs to be done in here, is to override one single method createButtonBar, and only for one, yet critical to our expectations, purpose: to change the way the button bar layouts its children (i.e. buttons). In this particular case I used the string '"L_O_R"' (not LOTR ;-).

So what is it? It's not clearly documented, but if to look at ButtonBar Javadoc, you will notice these few lines:

Windows: L_E+U+FBXI_YNOCAH_R

Mac OS: L_HE+U+FBIX_NCYOA_R

Linux: L_HE+UNYACBXIO_R

and accompanying pictures of buttons laid out on these OS-es. Each letter represents button type, and as per ButtonData.OK_DONE OK_DONE type has assigned letter O, while L and R mean left and right position respectively. The underscore _ is a spacer or gap. So with the string L_O_R we are saying: put buttons marked as LEFT on the left, those marked as RIGHT on the right, and those marked as OK_DONE just in between (because two surrounding gaps). Of course you can mix other types as it pleases you, so for instance you could pass L_YXOCN_R or whatever else you need.

If the Dialog had setButtonOrder(String) method, it would be much simpler to achieve desired effect. Anyway it's simpler that creating artificial spacers or messing with horizontal translations.

Firstfoot answered 6/3, 2023 at 2:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.