JDK-8207759 : VK_ENTER not consumed by TextField when default Button exists
  • Type: Bug
  • Component: javafx
  • Sub-Component: controls
  • Affected Version: 9,10,openjfx11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: x86_64
  • Submitted: 2018-07-17
  • Updated: 2019-12-17
  • Resolved: 2019-12-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
openjfx14Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
When registering an onKeyPress() event handler on a TextField, we are unable to consume the event. Specifically, when consuming the [ENTER] key, the default button of the scene is triggered and the event is not consumed by the TextField.

REGRESSION : Last worked in version 8u162

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Enter text into the TextField and press the [ENTER] key.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The TextField's onKeyPressed() method should be called, printing "-> Enter" to the console and the KeyEvent should be consumed.
ACTUAL -
The onAction() method for the scene's default button, btnSave, is called first, printing "-> Save" to the console before triggering the onKeyPressed() method for the TextField.

---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple UI
        VBox root = new VBox(10);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // TextField
        TextField textField = new TextField();

        // Capture the KeyCode.ENTER key
        textField.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                System.out.println("-> Enter");
                event.consume();
            }
        });

        // Buttons
        Button btnCancel = new Button("Cancel");
        btnCancel.setCancelButton(true);
        btnCancel.setOnAction(e -> {
            System.out.println("-> Cancel");
            primaryStage.close();
        });

        Button btnSave = new Button("Save");
        btnSave.setDefaultButton(true);
        btnSave.setOnAction(e -> {
            System.out.println("-> Save");
            primaryStage.close();
        });

        ButtonBar buttonBar = new ButtonBar();
        buttonBar.getButtons().addAll(btnCancel, btnSave);

        root.getChildren().addAll(textField, buttonBar);

        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Consume Test");
        primaryStage.show();
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Currently adding a catch to the default button to determine if the TextField has focus:

btnSave.setOnAction(e -> {
            if (textField.isFocused()) return;
            System.out.println("-> Save");
            primaryStage.close();
});

FREQUENCY : always



Comments
Changeset: d2d44b4a Author: Jeanette Winzenburg <fastegal@openjdk.org> Committer: Ajit Ghaisas <aghaisas@openjdk.org> Date: 2019-12-17 11:44:42 +0000 URL: https://git.openjdk.java.net/jfx/commit/d2d44b4a
17-12-2019

The misbehavior is caused by behavior.forwardToParent(keyEvent) while the event is delivered: it's implemented to call parent.fireEvent(keyEvent) which builds a completely new eventDispatchChain that's nested into the currently delivering chain. This results in dispatching the key along all handlers in the nested chain (including the scene's accelerators) before delivering it to remaining handlers of the current chain. The fix is to - remove forwardToParent (and any getParent().fire(..)) from TextInputControlBehavior - change enter mapping to not autoconsume - change textFieldBehavior.fire to consume the KeyEvent if action is handled and do nothing otherwise (thus letting it bubble up the current chain)
16-10-2019

Notes on ESCAPE: - it's misbehaving the same way as ENTER (provided it's consumed in the keyPressed), that is cancelButton is triggered before consuming - it's triggered twice without consuming
16-10-2019

after further digging, it looks like the manual forwarding of the ENTER confuses the event dispatch so thoroughly that the setOn handler is called as the very last, even after the accelerator. see https://stackoverflow.com/a/51419618/203657 for some thoughts and a (dirty ;) patch
19-07-2018

the reason seems to be in TextFieldBehaviour.fire(): it forwards the Enter to its parent if the field has no action handler and the actionEvent is not consumed (the latter is never true because the event is copied during dispatch, but that's another story which indirectly leads to JDK-8207385). Options for a workaround in this case are: - set a dummy actionHandler on the field: textField.setOnAction(e -> {}) - set a marker flag in the field's properties to prevent the forward: textField.getProperties().put("TextInputControlBehavior.disableForwardToParent", true); this flag was added in the fix of JDK-8145515
18-07-2018

Issue is reproducible in both windows and linux. Its a regression introduced in JDK 9+82. Windows 10, 64-bit JDK results ---------------- 8u40-b25 : Pass 8u181-b13 : Pass 9+81 : Pass 9+82 : Fail <== Regression introduced here 9.0.4+11 : Fail 10.0.1+10 : Fail 11-ea+13 : Fail --------------
18-07-2018