Friday, December 23, 2011

(Pure Java) JavaFX 2.0 Menus

In recent posts on JavaFX, I have focused on using JavaFX 2.0's new Java APIs without use of the JavaFX 1.x's JavaFXScript and without use of JavaFX 2.0's new FXML. All of these examples have been compiled with the standard Java compiler and executed with the standard Java launcher. In this post, I continue the theme of using pure Java APIs supported by JavaFX 2.0 while demonstrating development of JavaFX 2.0 menus.

I list the entire code listing for this example later in this post, but I first show snippets of the code to make it easier to focus on each piece. A good starting point for using JavaFX 2.0 menus is to instantiate an instance of MenuBar. This is straightforward as shown next.

Instantiating a javafx.scene.control.MenuBar
final MenuBar menuBar = new MenuBar();

A MenuBar can contain Menu instances as its children and each Menu instance can have instances of MenuItem as its children. The next code listing demonstrates instantiation of a Menu, adding of MenuItem instances (or an instance of SeparatorMenuItem) to that Menu instance, and then adding the Menu instance to the instance of MenuBar.

Adding Newly Instantiated Menu and MenuItem Instances to MenuBar
// Prepare left-most 'File' drop-down menu
final Menu fileMenu = new Menu("File");
fileMenu.getItems().add(new MenuItem("New"));
fileMenu.getItems().add(new MenuItem("Open"));
fileMenu.getItems().add(new MenuItem("Save"));
fileMenu.getItems().add(new MenuItem("Save As"));
fileMenu.getItems().add(new SeparatorMenuItem());
fileMenu.getItems().add(new MenuItem("Exit"));
menuBar.getMenus().add(fileMenu);

The example above is too simplified for realistic uses. There are no event handlers or actions associated with clicking on any of the menu items and there are no ways to select the menu items via keystroke rather than via mouse clicking. The next code listing demonstrates instantiation of MenuItem instances that include more than just a text string. In this code listing, there is an example of using MenuItemBuilder to build a much more complex MenuItem that includes association to a key combination and includes an association to an action handler.

More Sophisticated MenuItem Instantiation with Keystroke and Event Associations
// Prepare 'Help' drop-down menu
final Menu helpMenu = new Menu("Help");
final MenuItem searchMenuItem = new MenuItem("Search");
searchMenuItem.setDisable(true);
helpMenu.getItems().add(searchMenuItem);
final MenuItem onlineManualMenuItem = new MenuItem("Online Manual");
onlineManualMenuItem.setVisible(false);
helpMenu.getItems().add(onlineManualMenuItem);
helpMenu.getItems().add(new SeparatorMenuItem());
final MenuItem aboutMenuItem =
   MenuItemBuilder.create()
                  .text("About")
                  .onAction(
                      new EventHandler<ActionEvent>()
                      {
                         @Override public void handle(ActionEvent e)
                         {
                            out.println("You clicked on About!");
                         }
                      })
                  .accelerator(
                      new KeyCodeCombination(
                         KeyCode.A, KeyCombination.CONTROL_DOWN))
                  .build();             
helpMenu.getItems().add(aboutMenuItem);
menuBar.getMenus().add(helpMenu);

Besides demonstrating MenuItemBuilder, associating a key combination (CTRL-A in this case) with a menu item, and associating an action with a menu item, this code example also demonstrates making a menu item disabled (grayed out) with setDisable(boolean) or making it not appear at all with setVisible(boolean). Although I could have specified disabling the menu item or making the menu item invisible with a MenuItemBuilder, I intentionally used "set" methods on the MenuItems in this example to contrast that approach with using the MenuItemBuilder.

For completeness, here is the entire code listing of my example.

JavaFxMenus.java (The Complete Listing)
package dustin.examples;

import static java.lang.System.out;

import javafx.application.Application;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

/**
 * Example of creating menus in JavaFX.
 * 
 * @author Dustin
 */
public class JavaFxMenus extends Application
{
   /**
    * Build menu bar with included menus for this demonstration.
    * 
    * @param menuWidthProperty Width to be bound to menu bar width.
    * @return Menu Bar with menus included.
    */
   private MenuBar buildMenuBarWithMenus(final ReadOnlyDoubleProperty menuWidthProperty)
   {
      final MenuBar menuBar = new MenuBar();

      // Prepare left-most 'File' drop-down menu
      final Menu fileMenu = new Menu("File");
      fileMenu.getItems().add(new MenuItem("New"));
      fileMenu.getItems().add(new MenuItem("Open"));
      fileMenu.getItems().add(new MenuItem("Save"));
      fileMenu.getItems().add(new MenuItem("Save As"));
      fileMenu.getItems().add(new SeparatorMenuItem());
      fileMenu.getItems().add(new MenuItem("Exit"));
      menuBar.getMenus().add(fileMenu);

      // Prepare 'Examples' drop-down menu
      final Menu examplesMenu = new Menu("JavaFX 2.0 Examples");
      examplesMenu.getItems().add(new MenuItem("Text Example"));
      examplesMenu.getItems().add(new MenuItem("Objects Example"));
      examplesMenu.getItems().add(new MenuItem("Animation Example"));
      menuBar.getMenus().add(examplesMenu);

      // Prepare 'Help' drop-down menu
      final Menu helpMenu = new Menu("Help");
      final MenuItem searchMenuItem = new MenuItem("Search");
      searchMenuItem.setDisable(true);
      helpMenu.getItems().add(searchMenuItem);
      final MenuItem onlineManualMenuItem = new MenuItem("Online Manual");
      onlineManualMenuItem.setVisible(false);
      helpMenu.getItems().add(onlineManualMenuItem);
      helpMenu.getItems().add(new SeparatorMenuItem());
      final MenuItem aboutMenuItem =
         MenuItemBuilder.create()
                        .text("About")
                        .onAction(
                            new EventHandler<ActionEvent>()
                            {
                               @Override public void handle(ActionEvent e)
                               {
                                  out.println("You clicked on About!");
                               }
                            })
                        .accelerator(
                            new KeyCodeCombination(
                               KeyCode.A, KeyCombination.CONTROL_DOWN))
                        .build();             
      helpMenu.getItems().add(aboutMenuItem);
      menuBar.getMenus().add(helpMenu);

      // bind width of menu bar to width of associated stage
      menuBar.prefWidthProperty().bind(menuWidthProperty);

      return menuBar;
   }

   /**
    * Start of JavaFX application demonstrating menu support.
    * 
    * @param stage Primary stage.
    */
   @Override
   public void start(final Stage stage)
   {
      stage.setTitle("Creating Menus with JavaFX 2.0");
      final Group rootGroup = new Group();
      final Scene scene = new Scene(rootGroup, 800, 400, Color.WHEAT);
      final MenuBar menuBar = buildMenuBarWithMenus(stage.widthProperty());
      rootGroup.getChildren().add(menuBar);
      stage.setScene(scene);
      stage.show();
   }

   /**
    * Main executable function for running examples.
    * 
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(final String[] arguments)
   {
      Application.launch(arguments);
   }
}

The next series of screen snapshots attempt to demonstrate what this application looks like when executed using the java launcher. The images show the initial appearance of the application, the drop-down menu presented when "File" menu is clicked on, the drop-down menu presented when the "Help" menu is clicked on, and finally an image that shows the message written to standard output when the "About" menu item is clicked on under the "Help" menu.

The code in the example featured in this post has numerous syntax features that should look familiar to Swing developers. In fact, many of the JavaFX classes used above have the same names as AWT classes and so care must be used to import the correct class when using the IDE's automatic import suggestions.

The example above also provides an example of JavaFX binding. In particular, the width of the menu bar is bound to the width of the stage's width. This is useful because it looks better to have the menu bar span the entire top of the visual rather than being just wide enough to hold the menu labels.

Building menus is fairly straightforward in JavaFX 2.0 and can be implemented using basic Java tools and the JavaFX 2.0 JAR.

No comments: