© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
K. Sharan, P. SpäthLearn JavaFX 17https://doi.org/10.1007/978-1-4842-7848-2_4

4. Managing Stages

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • How to get details of screens such as their number, resolutions, and dimensions

  • What a stage is in JavaFX and how to set bounds and styles of a stage

  • How to move an undecorated stage

  • How to set the modality and opacity of a stage

  • How to resize a stage and how to show a stage in full-screen mode

The examples of this chapter lie in the com.jdojo.stage package. In order for them to work, you must add a corresponding line to the module-info.java file:
...
opens com.jdojo.stage to javafx.graphics, javafx.base;
...

Knowing the Details of Your Screens

The Screen class in the javafx.stage package is used to get the details, for example, dots-per-inch (DPI) setting and dimensions of user screens (or monitors). If multiple screens are hooked up to a computer, one of the screens is known as the primary screen and others as nonprimary screens. You can get the reference of the Screen object for the primary monitor using the static getPrimary() method of the Screen class with the following code:
// Get the reference to the primary screen
Screen primaryScreen = Screen.getPrimary();
The static getScreens() method returns an ObservableList of Screen objects:
ObservableList<Screen> screenList = Screen.getScreens();
You can get the resolution of a screen in DPI using the getDpi() method of the Screen class as follows:
Screen primaryScreen = Screen.getPrimary();
double dpi = primaryScreen.getDpi();

You can use the getBounds() and getVisualBounds() methods to get the bounds and visual bounds, respectively. Both methods return a Rectangle2D object, which encapsulates the (x, y) coordinates of the upper-left and the lower-right corners, the width, and the height of a rectangle. The getMinX() and getMinY() methods return the x and y coordinates of the upper-left corner of the rectangle, respectively. The getMaxX() and getMaxY() methods return the x and y coordinates of the lower-right corner of the rectangle, respectively. The getWidth() and getHeight() methods return the width and height of the rectangle, respectively.

The bounds of a screen cover the area that is available on the screen. The visual bounds represent the area on the screen that is available for use, after taking into account the area used by the native windowing system such as task bars and menus. Typically, but not necessarily, the visual bounds of a screen represent a smaller area than its bounds.

If a desktop spans multiple screens, the bounds of the nonprimary screens are relative to the primary screen. For example, if a desktop spans two screens with the (x, y) coordinates of the upper-left corner of the primary screen at (0, 0) and its width 1600, the coordinates of the upper-left corner of the second screen would be (1600, 0).

The program in Listing 4-1 prints the screen details when it was run on a Windows desktop with two screens. You may get a different output. Notice the difference in height for bounds and visual bounds for one screen and not for the other. The primary screen displays a task bar at the bottom that takes away some part of the height from the visual bounds. The nonprimary screen does not display a task bar, and therefore its bounds and visual bounds are the same.

Tip

Although it is not mentioned in the API documentation for the Screen class, you cannot use this class until the JavaFX launcher has started. That is, you cannot get screen descriptions in a non-JavaFX application. This is the reason that you would write the code in the start() method of a JavaFX application class. There is no requirement that the Screen class needs to be used on the JavaFX Application Thread. You could also write the same code in the init() method of your class.

// ScreenDetailsApp.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.geometry.Rectangle2D;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class ScreenDetailsApp extends Application  {
       public static void main(String[] args) {
               Application.launch(args);
       }
       public void start(Stage stage) {
               ObservableList<Screen> screenList = Screen.getScreens();
               System.out.println("Screens Count: " + screenList.size());
               // Print the details of all screens
               for(Screen screen: screenList) {
                      print(screen);
               }
               Platform.exit();
       }
       public void print(Screen s) {
               System.out.println("DPI: " + s.getDpi());
               System.out.print("Screen Bounds: ");
               Rectangle2D bounds = s.getBounds();
               print(bounds);
               System.out.print("Screen Visual Bounds: ");
               Rectangle2D visualBounds = s.getVisualBounds();
               print(visualBounds);
               System.out.println("-----------------------");
       }
       public void print(Rectangle2D r) {
               System.out.format("minX=%.2f, minY=%.2f, width=%.2f,
                        height=%.2f%n",
                        r.getMinX(), r.getMinY(),
                        r.getWidth(), r.getHeight());
       }
}
Screens Count: 2
DPI: 96.0
Screen Bounds: minX=0.00, minY=0.00, width=1680.00, height=1050.00
Screen Visual Bounds: minX=0.00, minY=0.00, width=1680.00, height=1022.00
-----------------------
DPI: 96.0
Screen Bounds: minX = 1680.00, minY=0.00, width= 1680.00, height=1050.00
Screen Visual Bounds: minX = 1680.00, minY=0.00, width= 1680.00, height=1050.0
-----------------------
Listing 4-1

Accessing Screen Details

What Is a Stage?

A stage in JavaFX is a top-level container that hosts a scene, which consists of visual elements. The Stage class in the javafx.stage package represents a stage in a JavaFX application. The primary stage is created by the platform and passed to the start(Stage s) method of the Application class. You can create additional stages as needed.

Tip

A stage in a JavaFX application is a top-level container. This does not necessarily mean that it is always displayed as a separate window. For this book’s purpose however, a stage corresponds to a window, unless otherwise noted.

Figure 4-1 shows the class diagram for the Stage class, which inherits from the Window class. The Window class is the superclass for several window-line container classes. It contains the basic functionalities that are common to all types of windows (e.g., methods to show and hide the window; set x, y, width, and height properties; set the opacity of the window; etc.). The Window class defines x, y, width, height, and opacity properties. It has show() and hide() methods to show and hide a window, respectively. The setScene() method of the Window class sets the scene for a window. The Stage class defines a close() method, which has the same effect as calling the hide() method of the Window class.
Figure 4-1

The class diagram for the Stage class

A Stage object must be created and modified on the JavaFX Application Thread. Recall that the start() method of the Application class is called on the JavaFX Application Thread, and a primary Stage is created and passed to this method. Note that the primary stage that is passed the start() method is not shown. You need to call the show() method to show it.

Several aspects of working with stages need to be discussed. I will handle them one by one from the basic to the advanced level in the sections that follow.

Showing the Primary Stage

Let’s start with the simplest JavaFX application, as shown in Listing 4-2. The start() method has no code. When you run the application, you do not see a window, nor do you see output on the console. The application runs forever. You will need to use the system-specific keys to cancel the application. If you are using Windows, use your favorite key combination Ctrl + Alt + Del to activate the task manager! If you are using the command prompt, use Ctrl + C.
// EverRunningApp.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.stage.Stage;
public class EverRunningApp extends Application {
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               // Do not write any code here
       }
}
Listing 4-2

An Ever-Running JavaFX Application

To determine what is wrong with the program in Listing 4-2, you need to understand what the JavaFX application launcher does. Recall that the JavaFX Application Thread is terminated when the Platform.exit() method is called or the last shown stage is closed. The JVM terminates when all nondaemon threads die. The JavaFX Application Thread is a nondaemon thread. The Application.launch() method returns when the JavaFX Application Thread terminates. In the preceding example, there is no way to terminate the JavaFX Application Thread. This is the reason the application runs forever.

Using the Platform.exit() method in the start() method will fix the problem. The modified code for the start() method is shown in Listing 4-3. When you run the program, it exits without doing anything meaningful.
// ShortLivedApp.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
public class ShortLivedApp extends Application {
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               Platform.exit(); // Exit the application
       }
}
Listing 4-3

A Short-Lived JavaFX Application

Let’s try to fix the ever-running program by closing the primary stage. You have only one stage when the start() method is called, and closing it should terminate the JavaFX Application Thread. Let’s modify the start() method of the EverRunningApp with the following code:
@Override
public void start(Stage stage) {
       stage.close(); // Close the only stage you have
}
Even with this code for the start() method, the EverRunningApp runs forever. The close() method does not close the stage if the stage is not showing. The primary stage was never shown. Therefore, adding a stage.close() call to the start() method did not do any good. The following code for the start() method would work. However, this will cause the screen to flicker as the stage is shown and closed:
@Override
public void start(Stage stage) {
       stage.show();  // First show the stage
       stage.close(); // Now close it
}
Tip

The close() method of the Stage class has the same effect as calling the hide() method of the Window class. The JavaFX API documentation does not mention that attempting to close a not showing window has no effect.

Setting the Bounds of a Stage

The bounds of a stage consist of four properties: x, y, width, and height. The x and y properties determine the location (or position) of the upper-left corner of the stage. The width and height properties determine its size. In this section, you will learn how to position and size a stage on the screen. You can use the getters and setters for these properties to get and set their values.

Let’s start with a simple example as shown in Listing 4-4. The program sets the title for the primary stage before showing it. When you run this code, you would see a window with the title bar, borders, and an empty area. If other applications are open, you can see their content through the transparent area of the stage. The position and size of the window are decided by the platform.

Tip

When a stage does not have a scene and its position and size are not set explicitly, its position and size are determined and set by the platform.

// BlankStage.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.stage.Stage;
public class BlankStage extends Application {
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               stage.setTitle("Blank Stage");
               stage.show();
       }
}
Listing 4-4

Displaying a Stage with No Scene and with the Platform Default Position and Size

Let’s modify the logic a bit. Here, you will set an empty scene to the stage without setting the size of the scene. The modified start() method would look as follows:
import javafx.scene.Group;
import javafx.scene.Scene;
...
@Override
public void start(Stage stage) {
       stage.setTitle("Stage with an Empty Scene");
       Scene scene = new Scene(new Group());
       stage.setScene(scene);
       stage.show();
}

Notice that you have set a Group with no children nodes as the root node for the scene, because you cannot create a scene without a root node. When you run the program in Listing 4-4 with the preceding code as its start() method, the position and size of the stage are determined by the platform. This time, the content area will have a white background, because the default background color for a scene is white.

Let’s modify the logic again. Here, let’s add a button to the scene. The modified start() method would be as follows:
import javafx.scene.control.Button;
...
@Override
public void start(Stage stage) {
       stage.setTitle("Stage with a Button in the Scene");
       Group root = new Group(new Button("Hello"));
       Scene scene = new Scene(root);
       stage.setScene(scene);
       stage.show();
}
When you run the program in Listing 4-4 with the preceding code as its start() method, the position and size of the stage are determined by the computed size of the scene. The content area of the stage is wide enough to show the title bar menus or the content of the scene, whichever is bigger. The content area of the stage is tall enough to show the content of the scene, which in this case has only one button. The stage is centered on the screen, as shown in Figure 4-2.
Figure 4-2

A stage with a scene that contains a button where the size of the scene is not specified

Let’s add another twist to the logic by adding a button to the scene and set the scene width and height to 300 and 100, respectively, as follows:
@Override
public void start(Stage stage) {
       stage.setTitle("Stage with a Sized Scene");
       Group root = new Group(new Button("Hello"));
       Scene scene = new Scene(root, 300, 100);
       stage.setScene(scene);
       stage.show();
}
When you run the program in Listing 4-4 with the preceding code as its start() method, the position and size of the stage are determined by the specified size of the scene. The content area of the stage is the same as the specified size of the scene. The width of the stage includes the borders on the two sides, and the height of the stage includes the height of the title bar and the bottom border. The stage is centered on the screen, as shown in Figure 4-3.
Figure 4-3

A stage with a scene with a specified size

Let’s add one more twist to the logic. You will set the size of the scene and the stage using the following code:
@Override
public void start(Stage stage) {
       stage.setTitle("A Sized Stage with a Sized Scene");
       Group root = new Group(new Button("Hello"));
       Scene scene = new Scene(root, 300, 100);
       stage.setScene(scene);
       stage.setWidth(400);
       stage.setHeight(100);
       stage.show();
}
When you run the program in Listing 4-4 with the preceding code as its start() method, the position and size of the stage are determined by the specified size of the stage. The stage is centered on the screen, and it will then look like the one shown in Figure 4-4.
Figure 4-4

A sized stage with a sized scene

Tip

The default centering of a stage centers it horizontally on the screen. The y coordinate of the upper-left corner of the stage is one-third of the height of the screen minus the height of the stage. This is the logic used in the centerOnScreen() method in the Window class.

Let me recap the rules for positioning and resizing a stage. If you do not specify the bounds of a stage and
  • It has no scene, its bounds are determined by the platform.

  • It has a scene with no visual nodes, its bounds are determined by the platform. In this case, the size of the scene is not specified.

  • It has a scene with some visual nodes, its bounds are determined by the visual nodes in the scene. In this case, the size of the scene is not specified, and the stage is centered on the screen.

  • It has a scene and the size of the scene is specified, its bounds are determined by the specified size of the scene. The stage is centered on the screen.

If you specify the size of the stage but not its position, the stage is sized according to the set size and centered on the screen, irrespective of the presence of a scene and the size of the scene. If you specify the position of the stage (x, y coordinates), it is positioned accordingly.

Tip

If you want to set the width and height of a stage to fit the content of its scene, use the sizeToScene() method of the Window class. The method is useful if you want to synchronize the size of a stage with the size of its scene after modifying the scene at runtime. Use the centerOnScreen() method of the Window class to center the stage on the screen.

If you want to center a stage on the screen horizontally as well as vertically, use the following logic:
Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
double x = bounds.getMinX() + (bounds.getWidth() - stage.getWidth())/2.0;
double y = bounds.getMinY() + (bounds.getHeight() - stage.getHeight())/2.0;
stage.setX(x);
stage.setY(y);
Be careful in using the preceding snippet of code. It makes use of the size of the stage. The size of a stage is not known until the stage is shown for the first time. Using the preceding logic before a stage is shown will not really center the stage on the screen. The following start() method of a JavaFX application will not work as intended:
@Override
public void start(Stage stage) {
       stage.setTitle("A Truly Centered Stage");
       Group root = new Group(new Button("Hello"));
       Scene scene = new Scene(root);
       stage.setScene(scene);
       // Wrong!!!! Use the logic shown below after the stage.show() call
       // At this point, stage width and height are not known. They are NaN.
       Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
       double x = bounds.getMinX() + (bounds.getWidth() –
                 stage.getWidth())/2.0;
       double y = bounds.getMinY() + (bounds.getHeight() –
                 stage.getHeight())/2.0;
       stage.setX(x);
       stage.setY(y);
       stage.show();
}

Initializing the Style of a Stage

The area of a stage can be divided into two parts: content area and decorations. The content area displays the visual content of its scene. Typically, decorations consist of a title bar and borders. The presence of a title bar and its content varies depending on the type of decorations provided by the platform. Some decorations provide additional features rather than just an aesthetic look. For example, a title bar may be used to drag a stage to a different location; buttons in a title bar may be used to minimize, maximize, restore, and close a stage; or borders may be used to resize a stage.

In JavaFX, the style attribute of a stage determines its background color and decorations. Based on styles, you can have the following five types of stages in JavaFX:
  • Decorated

  • Undecorated

  • Transparent

  • Unified

  • Utility

A decorated stage has a solid white background and platform decorations. An undecorated stage has a solid white background and no decorations. A transparent stage has a transparent background and no decorations. A unified stage has platform decorations and no border between the client area and decorations; the client area background is unified with the decorations. To see the effect of the unified stage style, the scene should be filled with Color.TRANSPARENT. Unified style is a conditional feature. A utility stage has a solid white background and minimal platform decorations.

Tip

The style of a stage specifies only its decorations. The background color is controlled by its scene background, which is solid white by default. If you set the style of a stage to TRANSPARENT, you will get a stage with a solid white background, which is the background of the scene. To get a truly transparent stage, you will need to set the background color of the scene to null using its setFill() method.

You can set the style of a stage using the initStyle(StageStyle style) method of the Stage class. The style of a stage must be set before it is shown for the first time. Setting it the second time, after the stage has been shown, throws a runtime exception. By default, a stage is decorated.

The five types of styles for a stage are defined as five constants in the StageStyle enum :
  • StageStyle.DECORATED

  • StageStyle.UNDECORATED

  • StageStyle.TRANSPARENT

  • StageStyle.UNIFIED

  • StageStyle.UTILITY

Listing 4-5 shows how to use these five styles for a stage. In the start() method, you need to uncomment only one statement at a time, which initializes the style of the stage. You will use a VBox to display two controls: a Label and a Button. The Label displays the style of the stage. The Button is provided to close the stage, because not all styles provide a title bar with a close button. Figure 4-5 shows the stage using four styles. The contents of windows in the background can be seen through a transparent stage. This is the reason that when you use the transparent style, you will see more content that has been added to the stage.
// StageStyleApp.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import static javafx.stage.StageStyle.DECORATED;
import static javafx.stage.StageStyle.UNDECORATED;
import static javafx.stage.StageStyle.TRANSPARENT;
import static javafx.stage.StageStyle.UNIFIED;
import static javafx.stage.StageStyle.UTILITY;
public class StageStyleApp extends Application {
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               // A label to display the style type
               Label styleLabel = new Label("Stage Style");
               // A button to close the stage
               Button closeButton = new Button("Close");
               closeButton.setOnAction(e -> stage.close());
               VBox root = new VBox();
               root.getChildren().addAll(styleLabel, closeButton);
               Scene scene = new Scene(root, 100, 70);
               stage.setScene(scene);
               // The title of the stage is not visible for all styles.
               stage.setTitle("The Style of a Stage");
               /* Uncomment one of the following statements at a time */
               this.show(stage, styleLabel, DECORATED);
               //this.show(stage, styleLabel, UNDECORATED);
               //this.show(stage, styleLabel, TRANSPARENT);
               //this.show(stage, styleLabel, UNIFIED);
               //this.show(stage, styleLabel, UTILITY);
       }
       private void show(Stage stage, Label styleLabel, StageStyle style) {
               // Set the text for the label to match the style
               styleLabel.setText(style.toString());
               // Set the style
               stage.initStyle(style);
               // For a transparent style, set the scene fill to null.
               // Otherwise, the content area will have the default white
               // background of the scene.
               if (style == TRANSPARENT) {
                      stage.getScene().setFill(null);
                      stage.getScene().getRoot().setStyle(
                             "-fx-background-color: transparent");
               } else if(style == UNIFIED) {
                      stage.getScene().setFill(Color.TRANSPARENT);
               }
               // Show the stage
               stage.show();
       }
}
Listing 4-5

Using Different Styles for a Stage

Figure 4-5

A stage using different styles

Moving an Undecorated Stage

You can move a stage to a different location by dragging its title bar. In an undecorated or transparent stage, a title bar is not available. You need to write a few lines of code to let the user move this kind of stage by dragging the mouse over the scene area. Listing 4-6 shows how to write the code to support dragging of a stage. If you change the stage to be transparent, you will need to drag the stage by dragging the mouse over only the message label, as the transparent area will not respond to the mouse events.

This example uses mouse event handling. I will cover event handling in detail in Chapter 9. It is briefly presented here to complete the discussion on using different styles of a stage.
// DraggingStage.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class DraggingStage extends Application {
       private Stage stage;
       private double dragOffsetX;
       private double dragOffsetY;
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               // Store the stage reference in the instance variable to
               // use it in the mouse pressed event handler later.
               this.stage = stage;
               Label msgLabel = new Label(
                        "Press the mouse button and drag.");
               Button closeButton = new Button("Close");
               closeButton.setOnAction(e -> stage.close());
               VBox root = new VBox();
               root.getChildren().addAll(msgLabel, closeButton);
               Scene scene = new Scene(root, 300, 200);
               // Set mouse pressed and dragged even handlers for the
               // scene
               scene.setOnMousePressed(e -> handleMousePressed(e));
               scene.setOnMouseDragged(e -> handleMouseDragged(e));
               stage.setScene(scene);
               stage.setTitle("Moving a Stage");
               stage.initStyle(StageStyle.UNDECORATED);
               stage.show();
       }
       protected void handleMousePressed(MouseEvent e) {
               // Store the mouse x and y coordinates with respect to the
               // stage in the reference variables to use them in the
               // drag event
               this.dragOffsetX = e.getScreenX() - stage.getX();
               this.dragOffsetY = e.getScreenY() - stage.getY();
       }
       protected void handleMouseDragged(MouseEvent e) {
               // Move the stage by the drag amount
               stage.setX(e.getScreenX() - this.dragOffsetX);
               stage.setY(e.getScreenY() - this.dragOffsetY);
       }
}
Listing 4-6

Dragging a Stage

The following snippet of code adds the mouse pressed and mouse dragged event handlers to the scene:
scene.setOnMousePressed(e -> handleMousePressed(e));
scene.setOnMouseDragged(e -> handleMouseDragged(e));
When you press the mouse in the scene (except the button area), the handleMousePressed() method is called. The getScreenX() and getScreenY() methods of the MouseEvent object return the x and y coordinates of the mouse with respect to the upper-left corner of the screen. Figure 4-6 shows a diagrammatic view of the coordinate systems. It shows a thin border around the stage. However, when you run the example code, you will not see any border. This is shown here to distinguish the screen area from the stage area. You store the x and y coordinates of the mouse with respect to the stage’s upper-left corner in instance variables.
Figure 4-6

Computing the mouse coordinates with respect to the stage

When you drag the mouse, the handleMouseDragged() method is called. The method computes and sets the position of the stage using the position of the mouse when it was pressed and its position during the drag.

Initializing Modality of a Stage

In a GUI application, you can have two types of windows: modal and modeless. When a modal window is displayed, the user cannot work with other windows in the application until the modal window is dismissed. If an application has multiple modeless windows showing, the user can switch between them at any time.

JavaFX has three types of modality for a stage:
  • None

  • Window modal

  • Application modal

The modality of a stage is defined by one of the following three constants in the Modality enum in the javafx.stage package:
  • NONE

  • WINDOW_MODAL

  • APPLICATION_MODAL

You can set the modality of a stage using the initModality(Modality m) method of the Stage class as follows:
// Create a Stage object and set its modality
Stage stage = new Stage();
stage.initModality(Modality.WINDOW_MODAL);
/* More code goes here.*/
// Show the stage
stage.show();
Tip

The modality of a stage must be set before it is shown. Setting the modality of a stage after it has been shown throws a runtime exception. Setting the modality for the primary stage also throws a runtime exception.

A Stage can have an owner. An owner of a Stage is another Window. You can set an owner of a Stage using the initOwner(Window owner) method of the Stage class. The owner of a Stage must be set before the stage is shown. The owner of a Stage may be null, and in this case, it is said that the Stage does not have an owner. Setting an owner of a Stage creates an owner-owned relationship. For example, a Stage is minimized or hidden if its owner is minimized or hidden, respectively.

The default modality of a Stage is NONE. When a Stage with the modality NONE is displayed, it does not block any other windows in the application. It behaves as a modeless window.

A Stage with the WINDOW_MODAL modality blocks all windows in its owner hierarchy. Suppose there are four stages: s1, s2, s3, and s4. Stages s1 and s4 have modalities set to NONE and do not have an owner; s1 is the owner of s2; s2 is the owner of s3. All four stages are displayed. If s3 has its modality set to WINDOW_MODAL, you can work with s3 or s4, but not with s2 and s1. The owner-owned relationship is defined as s1 to s2 to s3. When s3 is displayed, it blocks s2 and s1, which are in its owner hierarchy. Because s4 is not in the owner hierarchy of s3, you can still work with s4.

Tip

The modality of WINDOW_MODAL for a stage that has no owner has the same effect as if the modality is set to NONE.

If a Stage with its modality set to APPLICATION_MODAL is displayed, you must work with the Stage and dismiss it before you can work with any other windows in the application. Continuing with the same example from the previous paragraph of displaying four stages, if you set the modality of s4 to APPLICATION_MODAL, the focus will be set to s4, and you must dismiss it before you can work with other stages. Notice that an APPLICATION_MODAL stage blocks all other windows in the same application, irrespective of the owner-owned relationships.

Listing 4-7 shows how to use different modalities for a stage. It displays the primary stage with six buttons. Each button opens a secondary stage with a specified modality and owner. The text of the buttons tells you what kind of secondary stage they will open. When the secondary stage is shown, try clicking the primary stage. When the modality of the secondary stage blocks the primary stage, you will not be able to work with the primary stage; clicking the primary stage will set the focus back to the secondary stage.
// StageModalityApp.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Modality;
import static javafx.stage.Modality.NONE;
import static javafx.stage.Modality.WINDOW_MODAL;
import static javafx.stage.Modality.APPLICATION_MODAL;
import javafx.stage.Window;
public class StageModalityApp extends Application {
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               /* Buttons to display each kind of modal stage */
               Button ownedNoneButton = new Button("Owned None");
               ownedNoneButton.setOnAction(e -> showDialog(stage, NONE));
               Button nonOwnedNoneButton = new Button("Non-owned None");
               nonOwnedNoneButton.setOnAction(e ->
                        showDialog(null, NONE));
               Button ownedWinButton = new Button("Owned Window Modal");
               ownedWinButton.setOnAction(e ->
                        showDialog(stage, WINDOW_MODAL));
               Button nonOwnedWinButton =
                        new Button("Non-owned Window Modal");
               nonOwnedWinButton.setOnAction(e ->
                        showDialog(null, WINDOW_MODAL));
               Button ownedAppButton =
                        new Button("Owned Application Modal");
               ownedAppButton.setOnAction(e ->
                        showDialog(stage, APPLICATION_MODAL));
               Button nonOwnedAppButton =
                        new Button("Non-owned Application Modal");
               nonOwnedAppButton.setOnAction(e ->
                        showDialog(null, APPLICATION_MODAL));
               VBox root = new VBox();
               root.getChildren().addAll(
                        ownedNoneButton, nonOwnedNoneButton,
                        ownedWinButton, nonOwnedWinButton,
                        ownedAppButton, nonOwnedAppButton);
               Scene scene = new Scene(root, 300, 200);
               stage.setScene(scene);
               stage.setTitle("The Primary Stage");
               stage.show();
       }
       private void showDialog(Window owner, Modality modality) {
               // Create a Stage with specified owner and modality
               Stage stage = new Stage();
               stage.initOwner(owner);
               stage.initModality(modality);
               Label modalityLabel = new Label(modality.toString());
               Button closeButton = new Button("Close");
               closeButton.setOnAction(e -> stage.close());
               VBox root = new VBox();
               root.getChildren().addAll(modalityLabel, closeButton);
               Scene scene = new Scene(root, 200, 100);
               stage.setScene(scene);
               stage.setTitle("A Dialog Box");
               stage.show();
       }
}
Listing 4-7

Using Different Modalities for a Stage

Setting the Opacity of a Stage

The opacity of a stage determines how much you can see through the stage. You can set the opacity of a stage using the setOpacity(double opacity) method of the Window class. Use the getOpacity() method to get the current opacity of a stage.

The opacity value ranges from 0.0 to 1.0. Opacity of 0.0 means the stage is fully translucent; opacity of 1.0 means the stage is fully opaque. Opacity affects the entire area of a stage, including its decorations. Not all JavaFX runtime platforms are required to support opacity. Setting opacity on the JavaFX platforms that do not support opacity has no effect. The following snippet of code sets the opacity of a stage to half-translucent:
Stage stage = new Stage();
stage.setOpacity(0.5); // A half-translucent stage

Resizing a Stage

You can set whether a user can or cannot resize a stage by using its setResizable(boolean resizable) method. Note that a call to the setResizable() method is a hint to the implementation to make the stage resizable. By default, a stage is resizable. Sometimes, you may want to restrict the use to resize a stage within a range of width and height. The setMinWidth(), setMinHeight(), setMaxWidth(), and setMaxHeight() methods of the Stage class let you set the range within which the user can resize a stage.

Tip

Calling the setResizable(false) method on a Stage object prevents the user from resizing the stage. You can still resize the stage programmatically.

It is often required to open a window that takes up the entire screen space. To achieve this, you need to set the position and size of the window to the available visual bounds of the screen. Listing 4-8 provides the program to illustrate this. It opens an empty stage, which takes up the entire visual area of the screen.
// MaximizedStage.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class MaximizedStage extends Application {
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               stage.setScene(new Scene(new Group()));
               stage.setTitle("A Maximized Stage");
               // Set the position and size of the stage equal to the
               // position and size of the screen
               Rectangle2D visualBounds =
                        Screen.getPrimary().getVisualBounds();
               stage.setX(visualBounds.getMinX());
               stage.setY(visualBounds.getMinY());
               stage.setWidth(visualBounds.getWidth());
               stage.setHeight(visualBounds.getHeight());
               // Show the stage
               stage.show();
       }
}
Listing 4-8

Opening a Stage to Take Up the Entire Available Visual Screen Space

Showing a Stage in Full-Screen Mode

The Stage class has a fullScreen property that specified whether a stage should be displayed in full-screen mode. The implementation of full-screen mode depends on the platform and profile. If the platform does not support full-screen mode, the JavaFX runtime will simulate it by displaying the stage maximized and undecorated. A stage may enter full-screen mode by calling the setFullScreen(true) method. When a stage enters full-screen mode, a brief message is displayed about how to exit the full-screen mode: you will need to press the ESC key to exit full-screen mode. You can exit full-screen mode programmatically by calling the setFullScreen(false) method. Use the isFullScreen() method to check if a stage is in full-screen mode.

Showing a Stage and Waiting for It to Close

You often want to display a dialog box and suspend further processing until it is closed. For example, you may want to display a message box to the user with options to click yes and no buttons, and you want different actions performed based on which button is clicked by the user. In this case, when the message box is displayed to the user, the program must wait for it to close before it executes the next sequence of logic. Consider the following pseudo-code:
Option userSelection = messageBox("Close", "Do you want to exit?", YESNO);
if (userSelection == YES) {
       stage.close();
}

In this pseudo-code, when the messageBox() method is called, the program needs to wait to execute the subsequent if statement until the message box is dismissed.

The show() method of the Window class returns immediately, making it useless to open a dialog box in the preceding example. You need to use the showAndWait() method , which shows the stage and waits for it to close before returning to the caller. The showAndWait() method stops processing the current event temporarily and starts a nested event loop to process other events.

Tip

The showAndWait() method must be called on the JavaFX Application Thread. It should not be called on the primary stage or a runtime exception will be thrown.

You can have multiple stages open using the showAndWait() method. Each call to the method starts a new nested event loop. A specific call to the method returns to the caller when all nested event loops created after this method call have terminated.

This rule may be confusing in the beginning. Let’s look at an example to explain this in detail. Suppose you have three stages: s1, s2, and s3. Stage s1 is opened using the call s1.showAndWait(). From the code in s1, stage s2 is opened using the call s2.showAndWait(). At this point, there are two nested event loops: one created by s1.showAndWait() and another by s2.showAndWait(). The call to s1.showAndWait() will return only after both s1 and s2 have been closed, irrespective of the order they were closed. The s2.showAndWait() call will return after s2 has been closed.

Listing 4-9 contains a program that will allow you to play with the showAndWait() method call using multiple stages. The primary stage is opened with an Open button. Clicking the Open button opens a secondary stage using the showAndWait() method. The secondary stage has two buttons—Say Hello and Open—which will, respectively, print a message on the console and open another secondary stage. A message is printed on the console before and after the call to the showAndWait() method . You need to open multiple secondary stages, print messages by clicking the Say Hello button, close them in any order you want, and then look at the output on the console.
// ShowAndWaitApp.java
package com.jdojo.stage;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ShowAndWaitApp extends Application {
       protected static int counter = 0;
       protected Stage lastOpenStage;
       public static void main(String[] args) {
               Application.launch(args);
       }
       @Override
       public void start(Stage stage) {
               VBox root = new VBox();
               Button openButton = new Button("Open");
               openButton.setOnAction(e -> open(++counter));
               root.getChildren().add(openButton);
               Scene scene = new Scene(root, 400, 400);
               stage.setScene(scene);
               stage.setTitle("The Primary Stage");
               stage.show();
               this.lastOpenStage = stage;
       }
       private void open(int stageNumber) {
               Stage stage = new Stage();
               stage.setTitle("#" + stageNumber);
               Button sayHelloButton = new Button("Say Hello");
               sayHelloButton.setOnAction(
                  e -> System.out.println(
                        "Hello from #" + stageNumber));
               Button openButton = new Button("Open");
               openButton.setOnAction(e -> open(++counter));
               VBox root = new VBox();
               root.getChildren().addAll(sayHelloButton, openButton);
               Scene scene = new Scene(root, 200, 200);
               stage.setScene(scene);
               stage.setX(this.lastOpenStage.getX() + 50);
               stage.setY(this.lastOpenStage.getY() + 50);
               this.lastOpenStage = stage;
               System.out.println("Before stage.showAndWait(): " +
                        stageNumber);
               // Show the stage and wait for it to close
               stage.showAndWait();
               System.out.println("After stage.showAndWait(): " +
                        stageNumber);
       }
}
Listing 4-9

Playing with the showAndWait() Call

Tip

JavaFX does not provide a built-in window that can be used as a dialog box (a message box or a prompt window). You can develop one by setting the appropriate modality for a stage and showing it using the showAndWait() method.

Summary

The Screen class in the javafx.stage package is used to obtain the details, such as the DPI setting and dimensions, of the user’s screens hooked to the machine running the program. If multiple screens are present, one of the screens is known as the primary screen, and the others are the nonprimary screens. You can get the reference of the Screen object for the primary monitor using the static getPrimary() method of the Screen class.

A stage in JavaFX is a top-level container that hosts a scene, which consists of visual elements. The Stage class in the javafx.stage package represents a stage in a JavaFX application. The primary stage is created by the platform and passed to the start(Stage s) method of the Application class. You can create additional stages as needed.

A stage has bounds that comprise its position and size. The bounds of a stage are defined by its four properties: x, y, width, and height. The x and y properties determine the location (or position) of the upper-left corner of the stage. The width and height properties determine its size.

The area of a stage can be divided into two parts: content area and decorations. The content area displays the visual content of its scene. Typically, decorations consist of a title bar and borders. The presence of a title bar and its content varies depending on the type of decorations provided by the platform. You can have five types of stages in JavaFX: decorated, undecorated, transparent, unified, and utility.

JavaFX allows you to have two types of windows: modal and modeless. When a modal window is displayed, the user cannot work with other windows in the application until the modal window is dismissed. If an application has multiple modeless windows showing, the user can switch between them at any time. JavaFX defines three types of modality for a stage: none, window modal, and application modal. A stage with none as its modality is a modeless window. A stage with window modal as its modality blocks all windows in its owner hierarchy. A stage with application modal as its modality blocks all other windows in the application.

The opacity of a stage determines how much you can see through the stage. You can set the opacity of a stage using its setOpacity(double opacity) method. The opacity value ranges from 0.0 to 1.0. Opacity of 0.0 means the stage is fully translucent; the opacity of 1.0 means the stage is fully opaque. Opacity affects the entire area of a stage, including its decorations.

You can set a hint whether a user can resize a stage by using its setResizable(boolean resizable) method. The setMinWidth(), setMinHeight(), setMaxWidth(), and setMaxHeight() methods of the Stage class let you set the range within which the user can resize a stage. A stage may enter full-screen mode by calling its setFullScreen(true) method.

You can use the show() and showAndWait() methods of the Stage class to show a stage. The show() method shows the stage and returns, whereas the showAndWait() method shows the stage and blocks until the stage is closed.

The next chapter will show you how to create scenes and work with scene graphs.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.110.152