This solution displays two text areas and a default button. When the user presses the tab key, the focus moves to the next control down. When the user presses the enter key, the default button is launched.
To achieve this behavior:
- Pressing the enter key for each text area gets into the event filter, copies it and targets the parent area of โโthe node text field (which contains the default OK button). This causes the OK button to fire by default when input is pressed anywhere on the form. The initial press of the enter key is consumed so that it does not cause a new line to be added to the text in the text area.
- Pressing a tab key for each text area gets into the filter, and the processed list of parent focus is processed to find the next custom control, and focus is requested for this control. The original tab key is used so that it does not add new text to the text in the text area.
The code uses functions implemented in Java 8, so Java 8 is required to execute it.

import javafx.application.Application; import static javafx.application.Application.launch; import javafx.beans.value.*; import javafx.collections.ObservableList; import javafx.event.*; import javafx.scene.*; import javafx.scene.control.*; import static javafx.scene.input.KeyCode.ENTER; import static javafx.scene.input.KeyCode.TAB; import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; import javafx.stage.*; public class TextAreaTabAndEnterHandler extends Application { final Label status = new Label(); public static void main(String[] args) { launch(args); } @Override public void start(final Stage stage) { final TextArea textArea1 = new TabAndEnterIgnoringTextArea(); final TextArea textArea2 = new TabAndEnterIgnoringTextArea(); final Button defaultButton = new Button("OK"); defaultButton.setDefaultButton(true); defaultButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { status.setText("Default Button Pressed"); } }); textArea1.textProperty().addListener(new ClearStatusListener()); textArea2.textProperty().addListener(new ClearStatusListener()); VBox layout = new VBox(10); layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;"); layout.getChildren().setAll( textArea1, textArea2, defaultButton, status ); stage.setScene( new Scene(layout) ); stage.show(); } class ClearStatusListener implements ChangeListener<String> { @Override public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) { status.setText(""); } } class TabAndEnterIgnoringTextArea extends TextArea { final TextArea myTextArea = this; TabAndEnterIgnoringTextArea() { addEventFilter(KeyEvent.KEY_PRESSED, new TabAndEnterHandler()); } class TabAndEnterHandler implements EventHandler<KeyEvent> { private KeyEvent recodedEvent; @Override public void handle(KeyEvent event) { if (recodedEvent != null) { recodedEvent = null; return; } Parent parent = myTextArea.getParent(); if (parent != null) { switch (event.getCode()) { case ENTER: if (event.isControlDown()) { recodedEvent = recodeWithoutControlDown(event); myTextArea.fireEvent(recodedEvent); } else { Event parentEvent = event.copyFor(parent, parent); myTextArea.getParent().fireEvent(parentEvent); } event.consume(); break; case TAB: if (event.isControlDown()) { recodedEvent = recodeWithoutControlDown(event); myTextArea.fireEvent(recodedEvent); } else { ObservableList<Node> children = parent.getChildrenUnmodifiable(); int idx = children.indexOf(myTextArea); if (idx >= 0) { for (int i = idx + 1; i < children.size(); i++) { if (children.get(i).isFocusTraversable()) { children.get(i).requestFocus(); break; } } for (int i = 0; i < idx; i++) { if (children.get(i).isFocusTraversable()) { children.get(i).requestFocus(); break; } } } } event.consume(); break; } } } private KeyEvent recodeWithoutControlDown(KeyEvent event) { return new KeyEvent( event.getEventType(), event.getCharacter(), event.getText(), event.getCode(), event.isShiftDown(), false, event.isAltDown(), event.isMetaDown() ); } } } }
An alternative solution would be to implement your own custom skin for TextArea, which includes new key handling behavior. I believe that such a process will be more complex than the solution presented here.
Update
One thing that I didn't like about my initial solution to this problem was that after the Tab or Enter key was activated, there was no way to start the default processing. Therefore, I updated the solution so that if the user holds the control key by pressing the Tab or Enter key, the default Tab or Enter operation will be performed. This updated logic allows the user to insert a new line or tab space into the text area by pressing CTRL + Enter or CTRL + Tab.
source share