Search code examples
javafxdialoglabelwidthalert

How to set JavaFX Dialog Label width


I'm trying to set the width of a JavaFX Dialog to fit my Text.

I know how to do it, but there is a "Fudge Factor" of 32 that I would like to understand.

Can anyone explain how I can determine the value empirically?

I'm using the Zulu OpenJDK 17 with bundled JavaFX under Windows 10.

Here's some example code:

import static javafx.scene.control.Alert.AlertType.INFORMATION;

import javafx.application.Application;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class AlertWide extends Application {

    private static final String INFO_TEXT = """
            A couple of lines...
            1) Line 1 is quite short
            2) Line 2 is too wide to fit in the standard Alert Label, so it needs to be widened manually, that is to say, once a year
            3) the last line
            """;

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

    @Override
    public void start(final Stage primaryStage) throws Exception {

        final var infoWidth = new Text(INFO_TEXT).getBoundsInLocal().getWidth();

        final var alert     = new Alert(INFORMATION);
        ;         alert.setContentText(INFO_TEXT);

        ;                                        alert.showAndWait();
        setLabelWidth(alert, infoWidth     );    alert.showAndWait();
        setLabelWidth(alert, infoWidth + 32);    alert.showAndWait(); // TODO why 32?
    }

    private void setLabelWidth(final Alert alert, final double preferredLabelWidth) {
        alert.getDialogPane().getChildren().forEach(node -> {
            if (node  instanceof  Label  nodeLabel) {
                nodeLabel.setPrefWidth(preferredLabelWidth);
            }
        });
    }
}

Solution

  • Having delved into the depths of Dialog, I found a very simple solution.
    Rather than iterating through the DialogPane's children, I simply replaced the Label with a new Instance.

    P.S. "replaced" is not strictly speaking correct: the built-in DialogPane Label is set to unmanaged & invisible & so takes no part in the rendering.

    The following was just fine for my purposes:

    import static javafx.scene.control.Alert.AlertType.INFORMATION;
    
    import javafx.application.Application;
    import javafx.scene.control.*;
    import javafx.stage.Stage;
    
    public class AlertWideSetContent extends Application {
    
        private static final String INFO_TEXT = """
                A couple of lines...
                1) Line 1 is quite short
                2) Line 2 is too wide to fit in the standard Alert Label, so it needs to be widened manually, that is to say, once a year
                3) the last line
                """;
    
        public static void main(final String[] args) {
            launch(args);
        }
    
        @Override
        public void start(final Stage primaryStage) throws Exception {
    
            final var alert = new Alert(INFORMATION);
            ;         alert.getDialogPane().setContent(new Label(INFO_TEXT));
            ;         alert.showAndWait();
        }
    }
    

    But, as @kleopatra observed, setting the Preferred Width to -1 (USE_COMPUTED_SIZE) does it too:

    import static javafx.scene.control.Alert.AlertType.INFORMATION;
    
    import javafx.application.Application;
    import javafx.scene.Node;
    import javafx.scene.control.*;
    import javafx.stage.Stage;
    
    public class AlertWideUseComputedSize extends Application {
    
        private static final String INFO_TEXT = """
                A couple of lines...
                1) Line 1 is quite short
                2) Line 2 is too wide to fit in the standard Alert Label, so it needs to be widened manually, that is to say, once a year
                3) the last line
                """;
    
        public static void main(final String[] args) {
            launch(args);
        }
    
        @Override
        public void start(final Stage primaryStage) throws Exception {
    
            final var alert = new Alert   (INFORMATION);
            ;         alert.setContentText(INFO_TEXT );
    
            for (final Node node : alert.getDialogPane().getChildren()) {
                if (node  instanceof  Label  nodeLabel) {
                    nodeLabel.setPrefWidth(Label.USE_COMPUTED_SIZE);
                }
            }
            alert.showAndWait();
        }
    }