trilogy
trilogy

Reputation: 1795

Prevent dialog from going off screen

I have a primaryStage which is fine. I have a dialog that opens fine centered on the primaryStage (I got this to work by making the owner to the dialog the primaryStage

dialog.setInitOwner(primaryStage).  

However, the dialog will go off-screen if the primaryStage is already close to the edge of the screen. How do I make the dialog (which has the primaryStage as the owner) not go off the screen if the owner is near the edge of the screen?

I've tried this:

    double x = dialog.getX();
    double y = dialog.getY();
    double w = dialog.getWidth();
    double h = dialog.getHeight();
    if (x < Main.bounds.getMinX())
    {
        dialog.setX(Main.bounds.getMinX());
    }
    if (x + w > Main.bounds.getMaxX())
    {
        dialog.setX(Main.bounds.getMaxX() - w);
    }
    if (y < Main.bounds.getMinY())
    {
        dialog.setY(Main.bounds.getMinY());
    }
    if (y + h > Main.bounds.getMaxY())
    {
        dialog.setY(Main.bounds.getMaxY() - h);
    }

    dialog.showAndWait();

Main.bounds is created by:

private static Bounds computeAllScreenBounds()
{
    double minX = Double.POSITIVE_INFINITY;
    double minY = Double.POSITIVE_INFINITY;
    double maxX = Double.NEGATIVE_INFINITY;
    double maxY = Double.NEGATIVE_INFINITY;
    for (Screen screen : Screen.getScreens())
    {
        Rectangle2D screenBounds = screen.getVisualBounds();
        if (screenBounds.getMinX() < minX)
        {
            minX = screenBounds.getMinX();
        }
        if (screenBounds.getMinY() < minY)
        {
            minY = screenBounds.getMinY();
        }
        if (screenBounds.getMaxX() > maxX)
        {
            maxX = screenBounds.getMaxX();
        }
        if (screenBounds.getMaxY() > maxY)
        {
            maxY = screenBounds.getMaxY();
        }
    }
    return new BoundingBox(minX, minY, maxX - minX, maxY - minY);

Trying this, doesn't change any behaviour. I feel it's because the dialog.getX() function returns a NaN... }

As requested, an example:

public class Test extends Application
{

    @Override
    public void start(Stage primaryStage)
    {
        Bounds bounds = computeAllScreenBounds();
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>()
        {

            @Override
            public void handle(ActionEvent event)
            {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setHeaderText("This is an alert!");
                alert.setContentText("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
                alert.initOwner(primaryStage);
                double x = alert.getX();
                double y = alert.getY();
                double w = alert.getWidth();
                double h = alert.getHeight();
                if (x < bounds.getMinX())
                {
                    alert.setX(bounds.getMinX());
                }
                if (x + w > bounds.getMaxX())
                {
                    alert.setX(bounds.getMaxX() - w);
                }
                if (y < bounds.getMinY())
                {
                    alert.setY(bounds.getMinY());
                }
                if (y + h > bounds.getMaxY())
                {
                    alert.setY(bounds.getMaxY() - h);
                }
                alert.showAndWait();

            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

    private static Bounds computeAllScreenBounds()
    {
        double minX = Double.POSITIVE_INFINITY;
        double minY = Double.POSITIVE_INFINITY;
        double maxX = Double.NEGATIVE_INFINITY;
        double maxY = Double.NEGATIVE_INFINITY;
        for (Screen screen : Screen.getScreens())
        {
            Rectangle2D screenBounds = screen.getVisualBounds();
            if (screenBounds.getMinX() < minX)
            {
                minX = screenBounds.getMinX();
            }
            if (screenBounds.getMinY() < minY)
            {
                minY = screenBounds.getMinY();
            }
            if (screenBounds.getMaxX() > maxX)
            {
                maxX = screenBounds.getMaxX();
            }
            if (screenBounds.getMaxY() > maxY)
            {
                maxY = screenBounds.getMaxY();
            }
        }
        return new BoundingBox(minX, minY, maxX - minX, maxY - minY);
    }
}

Another ex:

public class Test extends Application
{

    @Override
    public void start(Stage primaryStage)
    {
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>()
        {

            @Override
            public void handle(ActionEvent event)
            {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setHeaderText("This is an alert!");
                alert.setContentText("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
                alert.initOwner(primaryStage);
                alert.setOnShown(new EventHandler<DialogEvent>()
                {
                    @Override
                    public void handle(DialogEvent event)
                    {

                        double x = alert.getX();
                        double y = alert.getY();
                        double w = alert.getWidth();
                        double h = alert.getHeight();

                        //SHOWS ALL NaN NaN NaN NaN
                        System.out.println(x + " " + y + " " + w + " " + h);

                    }
                });
                alert.showAndWait();

            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}

Upvotes: 2

Views: 835

Answers (2)

kleopatra
kleopatra

Reputation: 51535

The (unexpected for me) problem with the onShown handler on the alert when using showAndWait is that at the time the handler is invoked the alert is not yet showing and not yet sized/located - which is somewhat crazy and probably might be considered a bug.

A way around is to listen to the showingProperty of the alert and do any size/location adjustments in that listener. Something like (just a poc, obviously)

alert.showingProperty().addListener((src, ov, nv) -> {
    double x = alert.getX();
    double y = alert.getY();
    double w = alert.getWidth();
    double h = alert.getHeight();

    // as example just adjust if location top/left is off 
    // production must cope with bottom/right off as well, obviously 
    if (x < 0) {
        alert.setWidth(w + x);
        alert.setY(0);
    }
    if (y <0) {
        alert.setHeight(h + y);
        alert.setY(0);
    }

});
alert.showAndWait();

FYI: I really think it's a bug, so filed an issue let's see what happens

Upvotes: 2

marcs
marcs

Reputation: 73

As you are positioning the dialog in the center of the stage.. the dialog could only be out of the screen if the stage is also (at least partly) outside of your screen.

Please see the following code example..

@Override
public void start(Stage stage) {

    Pane pane = new Pane();
    Scene scene = new Scene(pane, 800, 500);

    Button button = new Button("Alert");
    button.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setHeaderText("This is an alert!");
            alert.initOwner(stage);

            alert.setOnShown(new EventHandler<DialogEvent>() {
                @Override
                public void handle(DialogEvent event) {
                    System.out.println(alert.getOwner().getX());
                    System.out.println(alert.getOwner().getY());

                    //Values from screen
                    int screenMaxX = (int) Screen.getPrimary().getVisualBounds().getMaxX();
                    int screenMaxY = (int) Screen.getPrimary().getVisualBounds().getMaxY();

                    //Values from stage
                    int width = (int) stage.getWidth();
                    int height = (int) stage.getHeight();
                    int stageMaxX = (int) stage.getX();
                    int stageMaxY = (int) stage.getY();

                    //Maximal values your stage
                    int paneMaxX = screenMaxX - width;
                    int paneMaxY = screenMaxY - height;

                    //Check if the position of your stage is not out of screen 
                    if (stageMaxX > paneMaxX || stageMaxY > paneMaxY) {
                        //Set stage where ever you want
                    }
                }
            });

            alert.showAndWait();

        }
    });

    pane.getChildren().add(button);
    stage.setScene(scene);
    stage.show();
}

Upvotes: 1

Related Questions