© 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_5

5. Making Scenes

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • What a scene and a scene graph are in a JavaFX application

  • About different rendering modes of a scene graph

  • How to set the cursor for a scene

  • How to determine the focus owner in a scene

  • How to use the Platform and HostServices classes

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

What Is a Scene?

A scene represents the visual contents of a stage. The Scene class in the javafx.scene package represents a scene in a JavaFX program. A Scene object is attached to, at the most, one stage at a time. If an already attached scene is attached to another stage, it is first detached from the previous stage. A stage can have, at the most, one scene attached to it at any time.

A scene contains a scene graph that consists of visual nodes. In this sense, a scene acts as a container for a scene graph. A scene graph is a tree data structure whose elements are known as nodes. Nodes in a scene graph form a parent-child hierarchical relationship. A node in a scene graph is an instance of the javafx.scene.Node class. A node can be a branch node or a leaf node. A branch node can have children nodes, whereas a leaf node cannot. The first node in a scene graph is called the root node. The root node can have children nodes; however, it never has a parent node. Figure 5-1 shows the arrangement of nodes in a scene graph. Branch nodes are shown in rounded rectangles and leaf nodes in rectangles.
Figure 5-1

The arrangement of nodes in a scene graph

The JavaFX class library provides many classes to represent branch and leaf nodes in a scene graph. The Node class in the javafx.scene package is the superclass of all nodes in a scene graph. Figure 5-2 shows a partial class diagram for classes representing nodes.
Figure 5-2

A partial class diagram for the javafx.scene.Node class

A scene always has a root node . If the root node is resizable, for example, a Region or a Control, it tracks the size of the scene. That is, if the scene is resized, the resizable root node resizes itself to fill the entire scene. Based on the policy of a root node, the scene graph may be laid out again when the size of the scene changes.

A Group is a nonresizable Parent node that can be set as the root node of a scene. If a Group is the root node of a scene, the content of the scene graph is clipped by the size of the scene. If the scene is resized, the scene graph is not laid out again.

Parent is an abstract class. It is the base class for all branch nodes in a scene graph. If you want to add a branch node to a scene graph, use objects of one of its concrete subclasses, for example, Group, Pane, HBox, or VBox. Classes that are subclasses of the Node class, but not the Parent class, represent leaf nodes, for example, Rectangle, Circle, Text, Canvas, or ImageView. The root node of a scene graph is a special branch node that is the topmost node. This is the reason you use a Group or a VBox as the root node while creating a Scene object. I will discuss classes representing branch and leaf nodes in detail in Chapters 10 and 12. Table 5-1 lists some of the commonly used properties of the Scene class.
Table 5-1

Commonly Used Properties of the Scene Class

Type

Name

Property and Description

ObjectProperty<Cursor>

cursor

It defines the mouse cursor for the Scene.

ObjectProperty<Paint>

fill

It defines the background fill of the Scene.

ReadOnlyObjectProperty<Node>

focusOwner

It defines the node in the Scene that owns the focus.

ReadOnlyDoubleProperty

height

It defines the height of the Scene.

ObjectProperty<Parent>

root

It defines the root Node of the scene graph.

ReadOnlyDoubleProperty

width

It defines the width of the Scene.

ReadOnlyObjectProperty<Window>

window

It defines the Window for the Scene.

ReadOnlyDoubleProperty

x

It defines the horizontal location of the Scene on the Window.

ReadOnlyDoubleProperty

y

It defines the vertical location of the Scene on the window.

Graphics Rendering Modes

The scene graph plays a vital role in rendering the content of a JavaFX application on the screen. Typically, two types of APIs are used to render graphics on a screen:
  • Immediate mode API

  • Retained mode API

In the immediate mode API, the application is responsible for issuing the drawing commands when a frame is needed on the screen. The graphics are drawn directly on the screen. When the screen needs to be repainted, the application needs to reissue the drawing commands to the screen. Java2D is an example of the immediate mode graphics-rendering API.

In the retained mode API, the application creates and attaches drawing objects to a graph. The graphics library, not the application code, retains the graph in memory. Graphics are rendered on the screen by the graphics library when needed. The application is responsible only for creating the graphic objects—the “what” part; the graphics library is responsible for storing and rendering the graphics—the “when” and “how” parts. The retained mode rendering API relieves developers of writing the logic for rendering the graphics. For example, adding or removing part of a graphic from a screen is simple by adding or removing a graphic object from the graph using high-level APIs; the graphics library takes care of the rest. In comparison to the immediate mode, the retained mode API uses more memory, as the graph is stored in memory. The JavaFX scene graph uses retained mode APIs.

You might think that using the immediate mode API would always be faster than using the retained mode API because the former renders graphics directly on the screen. However, using the retained mode API opens the door for optimizations by the class library that is not possible in the immediate mode where every developer is in charge of writing the logic as to what and when it should be rendered.

Figures 5-3 and 5-4 illustrate how immediate and retained mode APIs work, respectively. They show how a text, Hello, and a hexagon are drawn on the screen using the two APIs.
Figure 5-3

An illustration of the immediate mode API

Figure 5-4

An illustration of the retained mode API

Setting the Cursor for a Scene

An instance of the javafx.scene.Cursor class represents a mouse cursor. The Cursor class contains many constants, for example, HAND, CLOSED_HAND, DEFAULT, TEXT, NONE, WAIT, for standard mouse cursors. The following snippet of code sets the WAIT cursor for a scene:
Scene scene;
...
scene.setCursor(Cursor.WAIT);
You can also create and set a custom cursor to a scene. The cursor(String name) static method of the Cursor class returns a standard cursor if the specified name is the name of a standard cursor. Otherwise, it treats the specified name as a URL for the cursor bitmap. The following snippet of code creates a cursor from a bitmap file named mycur.png, which is assumed to be in the CLASSPATH:
// Create a Cursor from a bitmap
URL url = getClass().getClassLoader().getResource("mycur.png");
Cursor myCur = Cursor.cursor(url.toExternalForm());
scene.setCursor(myCur);
// Get the WAIT standard cursor using its name
Cursor waitCur = Cursor.cursor("WAIT")
scene.setCursor(waitCur);

The Focus Owner in a Scene

Only one node in a scene can be the focus owner. The focusOwner property of the Scene class tracks the Node class that has the focus. Note that the focusOwner property is read-only. If you want a specific node in a scene to be the focus owner, you need to call the requestFocus() method of the Node class.

You can use the getFocusOwner() method of the Scene class to get the reference of the node having the focus in the scene. A scene may not have a focus owner, and in that case, the getFocusOwner() method returns null. For example, a scene does not have a focus owner when it is created but is not attached to a window.

It is important to understand the distinction between a focus owner and a node having focus. Each scene may have a focus owner. For example, if you open two windows, you will have two scenes, and you can have two focus owners. However, only one of the two focus owners can have the focus at a time. The focus owner of the active window will have the focus. To check if the focus owner node also has the focus, you need to use the focused property of the Node class. The following snippet of code shows the typical logic in using the focus owner:
Scene scene;
...
Node focusOwnerNode = scene.getFocusOwner();
if (focusOwnerNode == null) {
        // The scene does not have a focus owner
}
else if (focusOwnerNode.isFocused()) {
        // The focus owner is the one that has the focus
}
else {
        // The focus owner does not have the focus
}
Table 5-2

Methods of the Platform Class

Method

Description

void exit()

It terminates a JavaFX application.

boolean isFxApplicationThread()

It returns true if the calling thread is the JavaFX Application Thread. Otherwise, it returns false.

boolean isImplicitExit()

It returns the value of the implicit implicitExit attribute of the application. If it returns true, it means that the application will terminate after the last window is closed. Otherwise, you need to call the exit() method of this class to terminate the application.

boolean isSupported(ConditionalFeature feature)

It returns true if the specified conditional feature is supported by the platform. Otherwise, it returns false.

void runLater(Runnable runnable)

It executes the specified Runnable on the JavaFX Application Thread. The timing of the execution is not specified. The method posts the Runnable to an event queue and returns immediately. If multiple Runnables are posted using this method, they are executed in the order they are submitted to the queue.

void setImplicitExit(boolean value)

It sets the implicitExit attribute to the specified value.

Understanding the Platform Class

The Platform class in the javafx.application package is a utility class used to support platform-related functionalities. It consists of all static methods, which are listed in Table 5-2.

The runLater() method is used to submit a Runnable task to an event queue, so it is executed on the JavaFX Application Thread. JavaFX allows developers to execute some of the code only on the JavaFX Application Thread. Listing 5-1 creates a task in the init() method that is called on the JavaFX Launcher Thread. It uses the Platform.runLater() method to submit the task to be executed on the JavaFX Application Thread later.

Tip

Use the Platform.runLater() method to execute a task that is created on a thread other than the JavaFX Application Thread but needs to run on the JavaFX Application Thread.

// RunLaterApp.java
package com.jdojo.scene;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class RunLaterApp extends Application {
        public static void main(String[] args) {
                Application.launch(args);
        }
        @Override
        public void init() {
                System.out.println("init(): " +
                         Thread.currentThread().getName());
                // Create a Runnable task
                Runnable task = () ->
                         System.out.println("Running the task on the "
                       + Thread.currentThread().getName());
                // Submit the task to be run on the JavaFX Application
                // Thread
                Platform.runLater(task);
        }
        @Override
        public void start(Stage stage) throws Exception {
                stage.setScene(new Scene(new Group(), 400, 100));
                stage.setTitle("Using Platform.runLater() Method");
                stage.show();
        }
}
init(): JavaFX-Launcher
Running the task on the JavaFX Application Thread
Listing 5-1

Using the Platform.runLater() Method

Some features in a JavaFX implementation are optional (or conditional). They may not be available on all platforms. Using an optional feature on a platform that does not support the feature does not result in an error; the optional feature is simply ignored. Optional features are defined as enum constants in the ConditionalFeature enum in the javafx.application package, as listed in Table 5-3.
Table 5-3

Constants Defined in the ConditionalFeature Enum

Enum Constant

Description

EFFECT

Indicates the availability of filter effects, for example, reflection, shadow, etc.

INPUT_METHOD

Indicates the availability of the text input method.

SCENE3D

Indicates the availability of 3D features.

SHAPE_CLIP

Indicates the availability of clipping of a node against an arbitrary shape.

TRANSPARENT_WINDOW

Indicates the availability of the full window transparency.

Suppose your JavaFX application uses 3D GUI on user demand. You can write your logic for enabling 3D features as shown in the following code:
import javafx.application.Platform;
import static javafx.application.ConditionalFeature.SCENE3D;
...
if (Platform.isSupported(SCENE3D)) {
        // Enable 3D features
}
else {
        // Notify the user that 3D features are not available
}

Knowing the Host Environment

The HostServices class in the javafx.application package provides services related to the launching environment (desktop for this book) hosting the JavaFX application. You cannot create an instance of the HostServices class directly. The getHostServices() method of the Application class returns an instance of the HostServices class. The following is an example of how to get an instance of HostServices inside a class that inherits from the Application class:
HostServices host = getHostServices();
The HostServices class contains the following methods:
  • String getCodeBase()

  • String getDocumentBase()

  • String resolveURI(String base, String relativeURI)

  • void showDocument(String uri)

The getCodeBase() method returns the code base uniform resource identifier (URI) of the application. In a stand-alone mode, it returns the URI of the directory that contains the JAR file used to launch the application. If the application is launched using a class file, it returns an empty string.

The getDocumentBase() method returns the URI of the document base. It returns the URI of the current directory for the application launched in stand-alone mode.

The resolveURI() method resolves the specified relative URI with respect to the specified base URI and returns the resolved URI.

The showDocument() method opens the specified URI in a new browser window. Depending on the browser preference, it may open the URI in a new tab instead. The following snippet of code opens the Yahoo! home page:
getHostServices().showDocument("http://www.yahoo.com");
The program in Listing 5-2 uses all of the methods of the HostServices class. It shows a stage with two buttons and host details. One button opens the Yahoo! home page and another shows an alert box. The output shown on the stage will vary depending on how the application is launched.
// KnowingHostDetailsApp.java
package com.jdojo.scene;
import java.util.HashMap;
import java.util.Map;
import javafx.application.Application;
import javafx.application.HostServices;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class KnowingHostDetailsApp extends Application {
        public static void main(String[] args) {
                Application.launch(args);
        }
        @Override
        public void start(Stage stage) {
                String yahooURL = "http://www.yahoo.com";
                Button openURLButton = new Button("Go to Yahoo!");
                openURLButton.setOnAction(e →
                         getHostServices().showDocument(yahooURL));
                Button showAlert = new Button("Show Alert");
                showAlert.setOnAction(e -> showAlert());
                VBox root = new VBox();
                // Add buttons and all host related details to the VBox
                root.getChildren().addAll(openURLButton, showAlert);
                Map<String, String> hostdetails = getHostDetails();
                for(Map.Entry<String, String> entry :
                                hostdetails.entrySet()) {
                    String desc = entry.getKey() + ": " +
                              entry.getValue();
                    root.getChildren().add(new Label(desc));
                }
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.setTitle("Knowing the Host");
                stage.show();
        }
        protected Map<String, String> getHostDetails() {
                Map<String, String> map = new HashMap<>();
                HostServices host = this.getHostServices();
                String codeBase = host.getCodeBase();
                map.put("CodeBase", codeBase);
                String documentBase = host.getDocumentBase();
                map.put("DocumentBase", documentBase);
                String splashImageURI =
                         host.resolveURI(documentBase, "splash.jpg");
                map.put("Splash Image URI", splashImageURI);
                return map;
        }
        protected void showAlert() {
                Stage s = new Stage(StageStyle.UTILITY);
                s.initModality(Modality.WINDOW_MODAL);
                Label msgLabel = new Label("This is an FX alert!");
                Group root = new Group(msgLabel);
                Scene scene = new Scene(root);
                s.setScene(scene);
                s.setTitle("FX Alert");
                s.show();
        }
}
Listing 5-2

Knowing the Details of the Host Environment for a JavaFX Application

Summary

A scene represents the visual contents of a stage. The Scene class in the javafx.scene package represents a scene in a JavaFX program. A Scene object is attached to at the most one stage at a time. If an already attached scene is attached to another stage, it is first detached from the previous stage. A stage can have at the most one scene attached to it at any time.

A scene contains a scene graph that consists of visual nodes. In this sense, a scene acts as a container for a scene graph. A scene graph is a tree data structure whose elements are known as nodes. Nodes in a scene graph form a parent-child hierarchical relationship. A node in a scene graph is an instance of the javafx.scene.Node class. A node can be a branch node or a leaf node. A branch node can have children nodes, whereas a leaf node cannot. The first node in a scene graph is called the root node. The root node can have children nodes; however, it never has a parent node.

An instance of the javafx.scene.Cursor class represents a mouse cursor. The Cursor class contains many constants, for example, HAND, CLOSED_HAND, DEFAULT, TEXT, NONE, WAIT, for standard mouse cursors. You can set a cursor for the scene using the setCursor() method of the Scene class.

Only one node in a scene can be the focus owner. The read-only focusOwner property of the Scene class tracks the node that has the focus. If you want a specific node in a scene to be the focus owner, you need to call the requestFocus() method of the Node class. Each scene may have a focus owner. For example, if you open two windows, you will have two scenes, and you may have two focus owners. However, only one of the two focus owners can have the focus at a time. The focus owner of the active window will have the focus. To check if the focus owner node also has the focus, you need to use the focused property of the Node class.

The Platform class in the javafx.application package is a utility class used to support platform-related functionalities. It contains methods for terminating the application, checking if the code being executed is executed on the JavaFX Application Thread, and so on.

The HostServices class in the javafx.application package provides services related to the launching environment (desktop for this book) hosting the JavaFX application. You cannot create an instance of the HostServices class directly. The getHostServices() method of the Application class returns an instance of the HostServices class.

The next chapter will discuss nodes in detail.

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

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