Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do Mnemonics work with Radio Buttons

Tags:

java

javafx

I was expecting that when I use Alt+mnemonic that the related radio button would become focused and selected. My expectations are based on my knowledge of how mnemonics work in Swing. Are my expectation wrong?

In my example code below I set the number of each radio button to be the mnemonic.

  1. "Option 1" is selected when the program starts.
  2. When I use Alt+3, the program beeps at me, all the mnemonics become underlined and "Option 3" becomes focused but NOT selected. The GUI now looks like this:

http://i.stack.imgur.com/ip3om.png

  1. If I just type 2, "Option 2 becomes focused, but NOT selected.

Here is the code:

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.effect.*;
import javafx.scene.paint.*;
import javafx.beans.value.*;
import javafx.geometry.*;

import javafx.event.*;

public class RadioButtonSSCCE extends Application
{
    public Parent createScene()
    {
        ToggleGroup group = new ToggleGroup();

        RadioButton rb1 = new RadioButton( "Option _1" );
        rb1.setUserData("Option 1 selected");
        rb1.setToggleGroup( group );

        RadioButton rb2 = new RadioButton( "Option _2" );
        rb2.setUserData("Option 2 selected");
        rb2.setToggleGroup( group );

        RadioButton rb3 = new RadioButton( "Option _3" );
        rb3.setUserData("Option 3 selected");
        rb3.setToggleGroup( group );

        VBox root = new VBox(5);
        root.setAlignment(Pos.CENTER);
        root.getChildren().addAll(rb1, rb2, rb3);

        ChangeListener<Toggle> cl = new ChangeListener<Toggle>()
        {
            @Override
            public void changed(ObservableValue<? extends Toggle> ov, Toggle old_toggle, Toggle new_toggle)
            {
                if (group.getSelectedToggle() != null)
                {
                    String text = group.getSelectedToggle().getUserData().toString();
                    System.out.println( text );
                }
            }
        };
        group.selectedToggleProperty().addListener(cl);

        rb1.setSelected( true );

        return root;
    }

    @Override
    public void start(Stage primaryStage)
    {
        Scene scene = new Scene(createScene(), 125, 125);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Radio Button SSCCE");
        primaryStage.show();
    }

    public static void main(String[] args)
    {
        launch(args);
    }
}

I have found that in order to invoke the mnemonic I have to:

  1. press and release the Alt key. Now the mnemonic of each radio button is underlined.
  2. press the number and the radio button becomes focused. However, I now need to use the space bar to select the radio button.

Why do I need to press the space bar? Should the radio button not be selected when the mnemonic is pressed?

Also, why can't I just use Alt+mnemonic as a single key combination to select a radio button?

Is my code wrong?

Edit:

The action is the key to selecting the radio button. I created a generic action to be shared by all buttons:

EventHandler<ActionEvent> onAction = new EventHandler<ActionEvent>()
{
    @Override
    public void handle(ActionEvent e)
    {
        RadioButton button = (RadioButton)e.getSource();
        button.setSelected(true);
    }
};

rb1.setOnAction( onAction );
rb2.setOnAction( onAction );
rb3.setOnAction( onAction );

Since the radio buttons are part of a button group I didn't want the ability to unselect a button.

like image 600
camickr Avatar asked May 14 '15 16:05

camickr


2 Answers

The Mnemonic in JavaFX fires an ActionEvent targeted at the registered Node. In the case of RadioButton, the ActionEvent doesn't toggle selection. You can add a generic handler to the RadioButton to toggle on the ActionEvent like this (assuming this is JavaFX 8).

rb1.setOnAction(event -> rb1.setSelected(!rb1.isSelected()));
like image 165
OttPrime Avatar answered Oct 14 '22 17:10

OttPrime


As OttPrime notes in his answer and in the Mnemonic Javadoc:

When a Mnemonic is registered on a Scene, and the KeyCombination reaches the Scene unconsumed, then the target Node will be sent an ActionEvent.

Now I think this is kind of an issue, because you don't really want to have only an ActionEvent sent to the radio button, instead you want to fire() the radio button.

Toggles the state of the radio button if and only if the RadioButton has not already selected or is not part of a ToggleGroup.

Note: firing a radio button will also send an ActionEvent to the button.

Here is the Java 8u40 code for Button::fire

@Override public void fire() {
    if (!isDisabled()) {
        fireEvent(new ActionEvent());
    }
}

Here is the code for ToggleButton::fire

@Override public void fire() {
    // TODO (aruiz): if (!isReadOnly(isSelected()) {
    if (!isDisabled()) {
        setSelected(!isSelected());
        fireEvent(new ActionEvent());
    }
}

And here is the code for RadioButton::fire

@Override public void fire() {
    // we don't toggle from selected to not selected if part of a group
    if (getToggleGroup() == null || !isSelected()) {
        super.fire();
    }
}

Note: RadioButton is a ToggleButton, so the super.fire() call will invoke the ToggleButton fire() code.

So, a mnemonic for a button will be fine: All the mnemonic is doing is sending an ActionEvent to the button (which is exactly the same as the fire implementation for the button). However, ToggleButtons and RadioButtons do extra stuff in their fire methods for manipulating the selection state of the ToggleButton, so mnemonic invocations aren't going to do that.

Now, you might say, well that's OK, I'll just implement OttPrime's workaround:

rb1.setOnAction(event -> rb1.setSelected(!rb1.isSelected()));

This will be great when you select the button via a mnemonic, but (as far as I can see), it will break other functionality for button selection (for example, if you programmatically fire the radio button and, probably, also if you click on the radio button with a mouse). This is because the selection state of the button will change twice (once in the fire() code and again in your action code) and you probably don't want that. Unfortunately, I can't actually test this theory as I don't have access to a machine which can use mnemonics.

So, to answer your original question "How do Mnemonics work with Radio Buttons"? => They don't (at least in IMO, not in any way that really makes sense).

I'd advise just disabling Mnemonic parsing for radio buttons and toggle buttons.

like image 22
jewelsea Avatar answered Oct 14 '22 16:10

jewelsea