söndag 1 juli 2012

Java FX 2 - Custom shortcut button

For a while ago I was playing around with the mnemonic stuff in Java FX, http://loop81.blogspot.se/2011/12/javafx-2-mnemonic-what-eh.html. However the mnemonic was not what I was looking for. What I needed was a button with a shortcut assigned to enhance the usability of the application I was working on.

In this post we will look into how you can create a shortcut button which listen on a given key combination and underline the character in the button text which is used to trigger the shortcut.

The shortcut button we like to create.

In the image above you will see three different buttons. The first one uses the underline property to underline the text, the second is a image button, and the last one is the button we will create. The idea is to extend the Button class and add some logic which binds the first character in the button text to a shortcut. Let us have a look on the code which achieves this.

public abstract class ShortcutButton extends Button {
  private final String text;
  private final Image graphics;
  private final KeyCode keyCode;
  private final EventHandler<KeyEvent> keyEventHandler;

  public ShortcutButton(String text, Image graphics) {
    this.text = text;
    this.graphics = graphics;
        
    // Extract the key code from the first character in the text string.
    keyCode = KeyCode.valueOf(text.substring(0, 1).toUpperCase());
    
    // Create the key listener which we use to catch the shortcut key.
    keyEventHandler = new EventHandler<KeyEvent>() {
      @Override
      public void handle(KeyEvent event) {
        if (event.isControlDown() && event.getCode() == keyCode) {
          event.consume();
          requestFocus();
          onKeyEvent();
        }
      }
    };
    
    // Assign a change listener to the scene property so we can add our key listener. 
    sceneProperty().addListener(new ChangeListener<Scene>() {
      @Override
      public void changed(ObservableValue<? extends Scene> observableValue,                                            Scene fromScene, Scene toScene) {        
        getScene().addEventHandler(KeyEvent.KEY_PRESSED, keyEventHandler);
      }
    });
    
    initGraphis();
  }
  
  private void initGraphis() {
    HBox layout = new HBox(-1);
    Label labelShortcut = new Label(text.substring(0, 1));
    labelShortcut.setUnderline(true);
    
    Label labelText = new Label(text.substring(1, text.length()));
    
    Label labelGraphis = new Label(" ", new ImageView(graphics));
    labelGraphis.setContentDisplay(ContentDisplay.LEFT);
    labelGraphis.setGraphicTextGap(1);
    
    layout.getChildren().setAll(labelGraphis, labelShortcut, labelText);
    setGraphic(layout);
  }
  
  /** Called when the use has pressed Ctrl and the given key code.*/
  public abstract void onKeyEvent();
}

Note that some code is omitted and only the interesting parts are shown.

Line 12: Here we extract the first character of the button text and create a KeyCode of it. This will later be used to bind the key event to the shortcut.

Line 15-24: Defines a EventHandler for the KeyEvent. This event handler will catch all key events and trigger on our key code together with Ctrl . When the key combination is pressed we consume that event (will not be propagated any further) and call the function onKeyEvent(), where we can define the action to be taken.

Line 27-32: Here we use the Scene property to add a ChangeListener. When the button is added to a Scene a event will be thrown. We will be using the Scene to add our key event handler and since the Scene is null until the button is added to a Scene we use a ChangeListener instead of calling getScene(). Note that we use the KEY_PRESSED event instead of the KEY_RELEASED event. When using the KEY_RELEASED event we obvious have to wait for the key to be released and this will create a strange feeling where the shortcut feels slow. 

So far we have defined all the logic we need together to catch the key event and together with a implementation of the onKeyEvent() function on line 53 we only need some graphics.

Line 38: We defines a horizontal layout using a HBox with a negative spacing of -1 to make the text appear together.

Line 39-40: Here we take the first character and create a underlined text.

Line 42: Adds the rest of the text as a Label.

Line 44-46: Adds the image to the button. Here we use a little trick. To get the text to appear correctly and without to much space we use a label of one white space together with a text gap set to one. This will align the image correctly in the button. Without the text gap there would be a large space between the image and the text.

This is all you need to implement a custom key shortcut button. The usage of the button would look something like this:

ShortcutButton shortcutButton = new ShortcutButton("Shortcut", icon2) {
  @Override
  public void onKeyEvent() {
    System.out.println("User pressed: Ctrl + S");
  }
};

In this post you have seen how we can use the EventHandler, ChangeListener and the Scene property to create a simple key shortcut button. Of course the button is not so smart and it would be nice to have a builder for it, but that is a other story.

A complete Eclipse project with the button and a demo can be found at: http://files.loop81.com/rest/download/6ba18227-c06a-4503-a986-7996bbe6fa59

Inga kommentarer:

Skicka en kommentar