Enter Key Event Is Not Working On Dialog In Javafx? - javafx

I have tried the below code, it works fine with mouse event, but when I use key event i.e ENTER Key on any button than its not showing the result.
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle(null);
alert.setHeaderText(null);
alert.setGraphic(null);
alert.setContentText("Choose your option.");
ButtonType buttonTypeOne = new ButtonType("One");
ButtonType buttonTypeTwo = new ButtonType("Two");
ButtonType buttonTypeThree = new ButtonType("Three");
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne) {
System.out.println("One");
} else if (result.get() == buttonTypeTwo) {
System.out.println("Two");
} else if (result.get() == buttonTypeThree) {
System.out.println("Three");
}

I do not recommend making all buttons respond to enter, as that is counter to how most UI dialogs work.
Normally, a button with focus will fire when you press space, not enter. There are however special buttons that will activate on specific keys: A default button will fire on enter and a cancel button will fire on esc. Usually you will have only one of each of these special types of buttons in your dialog, so that they can be fired via the special keyboard accelerator, regardless of which button currently has focus.
Additionally, different desktop OS systems have different standards on placement of default and cancel buttons in a dialog system. This is to assist the user in easily finding these special buttons in any dialog. The JavaFX dialog system implements some logic internally for locating buttons in dialogs where the user would expect to see them across different desktop operating systems.
Let's say you want the button types from your example to be defined as default or cancel buttons and placed in the correct position for such buttons for your OS, then you can do as below:
ButtonType buttonTypeTwo = new ButtonType(
"Two",
ButtonBar.ButtonData.OK_DONE
);
ButtonType buttonTypeThree = new ButtonType(
"Three",
ButtonBar.ButtonData.CANCEL_CLOSE
);
Note the JavaFX system has automatically changed the position of the buttons and some of the color highlighting. When the user presses enter, then "Two" will fire, when the user presses esc, then "Three" will fire. If you run the same code on Windows or Linux, likely the buttons will be positioned differently, according to whatever button positioning standard is used for those OSes.
If you don't want JavaFX to reposition your buttons according to OS standards, but you want them to still respond to enter and esc keys, then you can lookup the buttons and directly modify the button attributes like below:
Button buttonTwo = (Button) alert.getDialogPane().lookupButton(buttonTypeTwo);
buttonTwo.setDefaultButton(true);
Button buttonThree = (Button) alert.getDialogPane().lookupButton(buttonTypeThree);
buttonThree.setCancelButton(true);
I recommend letting JavaFX appropriately position buttons of specific types rather than performing lookups as above.
I also recommend setting at least a CANCEL_CLOSE button or OK_DONE button in your JavaFX Alert, otherwise the user may have a difficult time actually closing the alert as the dialog will probably not respond to key presses as the user expects.

I don't know if this is not a kind of too much of a workaround, but at least it works:
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
[...]
ButtonType buttonTypeTwo = new ButtonType("Two");
ButtonType buttonTypeThree = new ButtonType("Three");
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree);
//Create a button for every ButtonType you add to your alert and give it a Eventhandler
Button button1 = (Button) alert.getDialogPane().lookupButton(buttonTypeOne);
Button button2 = (Button) alert.getDialogPane().lookupButton(buttonTypeTwo);
Button button3 = (Button) alert.getDialogPane().lookupButton(buttonTypeThree);
button1.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ENTER)
alert.setResult(buttonTypeOne);
}
});
button2.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ENTER)
alert.setResult(buttonTypeTwo);
}
});
button3.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ENTER)
alert.setResult(buttonTypeThree);
}
});
//go ahead with your code
Optional<ButtonType> result = alert.showAndWait();
[...]
You just create some Buttons and assigned them the actual buttons on your alert. In the next Step you can give every button an EventHandler which just (In this example) checks - when any key is released - if the key was ENTER and set the result.
I guess there are better solutions for this. But it's the easiest way which comes to my mind currently. Hope it helps you.

Here how i solved it.
it works fine below is my confirm alert function.
public static boolean confirmAlert(String title, String msg){
ButtonType buttonTypeYes = new ButtonType("Yes", ButtonBar.ButtonData.OK_DONE);
ButtonType buttonTypeNo = new ButtonType("No",ButtonBar.ButtonData.CANCEL_CLOSE);
Alert alert = new Alert(Alert.AlertType.NONE, msg,buttonTypeNo,buttonTypeYes);
alert.setTitle(title);
Button button1 = (Button) alert.getDialogPane().lookupButton(buttonTypeYes);
button1.setDefaultButton(false); //***set default to false***
Button button2 = (Button) alert.getDialogPane().lookupButton(buttonTypeNo);
button1.setOnKeyReleased(event -> {
if(event.getCode() == KeyCode.ENTER)
alert.setResult(buttonTypeYes);
});
button2.setOnKeyReleased(event -> {
if(event.getCode() == KeyCode.ENTER)
alert.setResult(buttonTypeNo);
});
alert.showAndWait();
return alert.getResult()==buttonTypeYes;
}

you don't need to set default method, On which ever button you want to enter key
Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION, "", ButtonType.YES, ButtonType.NO);
confirmAlert.setHeaderText("Are You Sure Want To Delete ?");
Button bt1 = (Button) confirmAlert.getDialogPane().lookupButton(ButtonType.YES);
Button bt2 = (Button) confirmAlert.getDialogPane().lookupButton(ButtonType.NO);
bt2.addEventHandler(KeyEvent.ANY,(event) -> {
if(event.getCode() == KeyCode.ENTER){
confirmAlert.setResult(ButtonType.NO);
}
});
Optional <ButtonType> action = confirmAlert.showAndWait();
if(action.get() == ButtonType.NO){
System.err.println("Action You Want");
}

Related

JavaFX Events for Mouse Interactions are not triggered if Key is pressed

JavaFX does not execute events like the ActionEvent for Button or CheckBox, if a modifier key like CTRL or SHIFT is pressed. As far as I understand this behavior is implemented in ButtonBehavior (e.g. note the expression ! keyDown in the following method from that class):
#Override public void mouseReleased(MouseEvent e) {
// if armed by a mouse press instead of key press, then fire!
final ButtonBase button = getControl();
if (! keyDown && button.isArmed()) {
button.fire();
button.disarm();
}
}
First of all, I do not really understand the reason for this. What is the purpose of not firing a button if a key is pressed?
This is my use-case: I want to implement a checkbox that can be checked/unchecked as normal. It will toggle some state in a model. But it should have an additional feature: if the user presses some key like CTRL while checking/unchecking with the mouse, an additional flag called "locked" or "protected" should be set in the model, which will prevent that the state can be overwritten by some other logic of the application.
This should give an idea about the use-case, but if not it doesn't really matter for my actual question: How can I make it possible that a CheckBox can still be toggled (or a Button be pressed) even though the user presses a modifier key?
Thanks for your help!
That is odd you can implement it yourself like so
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
CheckBox checkBox = new CheckBox();
checkBox.setOnMouseClicked(event -> {
if(event.isControlDown()) {
System.out.print("Control down click ");
checkBox.setSelected(!checkBox.isSelected());
}
else
System.out.print("Normal click ");
System.out.println("Checked Status:"+checkBox.isSelected());
});
Button button = new Button("Button");
button.setOnMouseClicked(event -> {
if(event.isControlDown())
System.out.println("Control down click");
else
System.out.println("Normal click");
});
vBox.getChildren().addAll(new Label("Click the box"),checkBox,button);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
The output for CheckBox:
Normal click Checked Status:true
Normal click Checked Status:false
Control down click Checked Status:true
Control down click Checked Status:false
The output for Button:
Normal click
Control down click

Open another window on clicking OK button of Alert in JavaFX

private void showAlert(Alert.AlertType alertType, Window owner, String title, String message) {
Alert alert = new Alert(alertType);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.initOwner(owner);
//alert.show();
Optional<ButtonType> result = alert.showAndWait();
if ((result.isPresent()) && (result.get() == ButtonType.OK))
{
System.out.println("ALL OK..!");
//Open another window on clicking the OK button
}
}
On this particular section, on clicking OK button, I want to open another window.
Usually Button type is used for which an event can be defined by
buttonName.setOnAction( event -> {
//Action to do
} );
Now how do I define this OK as a button Type?

How to make all buttons having ActionEvent handler handle Enter key in JavaFX?

I have about 50 buttons in my application. For all buttons I created handler this way:
#FXML
protected void handleFooButtonActionEvent(ActionEvent actionEvent) {
...
}
This way user can press buttons using mouse left button or Space key. However, it is normal practice (as I know) to allow user press button using Enter key. Is it possible to make all buttons having ActionEvent handler (see above) handle also Enter key in JavaFX, for example if we have reference to Stage stage, Scene scene or Parent root?
You can configure the Scene to do it:
scene.getAccelerators().put(
KeyCombination.valueOf("Enter"), () -> {
Node focusOwner = scene.getFocusOwner();
if (focusOwner instanceof Button) {
((Button) focusOwner).fire();
}
});
You can also do it with an event handler:
scene.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ENTER) {
Node focusOwner = scene.getFocusOwner();
if (focusOwner instanceof Button) {
((Button) focusOwner).fire();
}
}
});
Normally I’d agree with James_D that changing the standard behavior of buttons is not a good idea, but I’m finding that all GTK applications allow Enter to trigger a button press, as does Firefox as you’ve mentioned.

How to add error popup for empty TextField input in Scenebuilder

I am making an application using Scenebuilder with JavaFX.
I have three inputs for a TableView:
Two TextField input1, input2.
One DatePicker.
When one or more of the input fields is empty and i click on the addButton, the object is added to the TableView.
How do I show an error popup which appears whenever i click on addButton and at least one field (input1, input2) is empty ?
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
if ((input1.getText() != null && !input1.getText().isEmpty()) &&
(input2.getText() != null && !input2.getText().isEmpty())){
//ADD CODE TO ADD THE ITEM HERE!
} else {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Input fields empty");
alert.setContentText("Please fill all input fields");
alert.showAndWait();
}
}
});
PS : Here you can find different Alert Types depending on your needs.

How to get a JavaFX MenuItem to respond to a TAB KeyPress?

A JavaFX MenuItem can respond to most KeyPress events by setting an ActionEvent EventHandler. However, while the event handler does catch a KeyPress of KeyCode.ENTER, it does not catch a KeyCode.TAB KeyPress event. Apparently, some key events like TAB are handled at a deeper level. For example, the arrow keys enable traversal of the menu.
My ContextMenu is a list of completions of an email address string the user has started typing in a TextField. The users want to press the arrow keys to select the desired item, and the TAB key to execute the completion.
I can attach an event handler to the ContextMenu itself and catch the TAB keypress. But the event's Source is then the ContextMenu, and I can find no variables in the ContextMenu indicating which MenuItem was highlighted when the TAB key was pressed. MenuItem allows css style to control appearance of the menu item in focus, but it does not have any properties telling whether it is in focus or not.
I have tried futzing with the EventDispatchChain via MenuItem buildEventDispatchChain() to no avail. There seems to be no way to intercept the TAB KeyPress or otherwise determine which menu item was in focus when the TAB key was pressed.
Any suggestions?
If I get this right, you want to override the default keypressed listener to add your own response, so for that we have to find where it's applied.
To get this working, we've got to get our hands dirty with private API...
ContextMenu skin (ContextMenuSkin) uses a ContextMenuContent object, as a container with all the items. Each of these items are also in a ContextMenuContent.MenuItemContainer container.
We can override the keypressed listener on the parent container, while we can add a focusedProperty listener to the items on the items container.
Using this private API
import com.sun.javafx.scene.control.skin.ContextMenuContent;
this is working for me:
private ContextMenuContent.MenuItemContainer itemSelected=null;
#Override
public void start(Stage primaryStage) {
MenuItem cmItem1 = new MenuItem("Item 1");
cmItem1.setOnAction(e->System.out.println("Item 1"));
MenuItem cmItem2 = new MenuItem("Item 2");
cmItem2.setOnAction(e->System.out.println("Item 2"));
final ContextMenu cm = new ContextMenu(cmItem1,cmItem2);
Scene scene = new Scene(new StackPane(), 300, 250);
scene.setOnMouseClicked(t -> {
if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){
cm.show(scene.getWindow(),t.getScreenX(),t.getScreenY());
ContextMenuContent cmc= (ContextMenuContent)cm.getSkin().getNode();
cmc.setOnKeyPressed(ke->{
switch (ke.getCode()) {
case UP: break;
case DOWN: break;
case TAB: ke.consume();
if(itemSelected!=null){
itemSelected.getItem().fire();
}
cm.hide();
break;
default: break;
}
});
VBox itemsContainer = cmc.getItemsContainer();
itemsContainer.getChildren().forEach(n->{
ContextMenuContent.MenuItemContainer item=(ContextMenuContent.MenuItemContainer)n;
item.focusedProperty().addListener((obs,b,b1)->{
if(b1){
itemSelected=item;
}
});
});
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
Excellent! Thank you #jose! I ended up writing somewhat different code but
the key is using com.sun.javafx.scene.control.skin.ContextMenuContent, which provides
access to the ContextMenuContent.MenuItemContainer objects that hold the MenuItems.
In order to not break the existing UP/DOWN key behavior, I added a new handler
to the ContextMenuContent object; this handler only consumes the TAB KeyPress and
everthing else passes through to their normal handlers.
Looking at the ContextMenuContent class, I borrowed their existing method for
finding the focused item, so didn't have to add focusedProperty listeners.
Also, I'm on Java 1.7 and don't have lambdas and I use a very basic programming style.
public class MenuItemHandler_CMC <T extends Event> implements EventHandler {
public ContextMenuContent m_cmc;
public AddressCompletionMenuItemHandler_CMC(ContextMenuContent cmc){
m_cmc = cmc;
}
#Override
public void handle(Event event){
KeyEvent ke = (KeyEvent)event;
switch(ke.getCode()){
case TAB:
ke.consume();
MenuItem focused_menu_item = findFocusedMenuItem();
if(focused_menu_item != null){
focused_menu_item.fire();
}
break;
default: break;
}
}
public MenuItem findFocusedMenuItem() {
VBox items_container = m_cmc.getItemsContainer();
for (int i = 0; i < items_container.getChildren().size(); i++) {
Node n = items_container.getChildren().get(i);
if (n.isFocused()) {
ContextMenuContent.MenuItemContainer menu_item_container = (ContextMenuContent.MenuItemContainer)n;
MenuItem menu_item = menu_item_container.getItem();
return menu_item;
}
}
return null;
}
}
...Attach the additional handler
if(m_context_menu.getSkin() != null){
ContextMenuContent cmc = (ContextMenuContent)m_context_menu.getSkin().getNode();
MenuItemHandler_CMC menu_item_handler_cmc = new MenuItemHandler_CMC(cmc);
cmc.addEventHandler(KeyEvent.KEY_PRESSED, menu_item_handler_cmc);
}

Resources